<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Schallberts Blog</title><link>https://blog.schallbert.de/posts/</link><description>Recent content in Posts on Schallberts Blog</description><generator>Hugo</generator><language>de</language><lastBuildDate>Mon, 10 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.schallbert.de/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>htmltest-Konfiguration für Hugo</title><link>https://blog.schallbert.de/hugo-link-checker/</link><pubDate>Mon, 10 Aug 2026 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/hugo-link-checker/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-08-10-htmltest-success-output-cover.avif"&#10; class="post-cover"&#10; alt="Image: An SSH public key randomart image as visual fingerprint for humans to quickly see if public keys match"&#10; title="htmltest-Konfiguration für Hugo" /&gt;&#10;&lt;p&gt;Nun bin ich mit dem &lt;a href="https://blog.schallbert.de/projects/migrating-jekyll-to-hugo/"&gt;Umzug meiner Website zu Hugo&lt;/a&gt; fast fertig. Ich musste mehrere Anpassungen in der Frontmatter jedes Artikel vornehmen, damit sie in Hugo ähnliche Funktionen aufweisen wie zuvor mit Jekyll. Es ergaben sich einige Änderungen in der Ordnerstruktur teils durch die Migration, teils auch durch meine Erfahrungen als Autor, sodass auf dem Weg einige Links in praktisch jedem meiner Artikel zu überarbeiten waren. Inzwischen habe ich fast 340 davon, wenn man die Übersetzungen mitzählt.&lt;/p&gt;&#10;&lt;p&gt;Der schieren Masse der Links komme ich ohne weitere Hilfsmittel nicht bei. Also benötige ich Werkzeug.&lt;/p&gt;&#10;&lt;h2 id="htmltest"&gt;htmltest&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://github.com/wjdp/htmltest" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Htmltest&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist ein Hilfsprogramm, das HTML-Dateien nach Problemen durchsuchen kann. Unter anderem spürt es defekte Links auf. Ich installiere es per&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Red Hat-based Linux package manager install command&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo dnf install htmltest&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;und wende es auf die von Hugo &amp;ldquo;gebauten&amp;rdquo; HTMl-Dateien im &lt;code&gt;/public&lt;/code&gt;-Verzeichnis an:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;htmltest public/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;✘✘✘ failed in 2.890119823s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;2612&lt;/span&gt; errors in &lt;span style="color:#ae81ff"&gt;337&lt;/span&gt; documents&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Das sind aber ganz schön viele Fehler! Die Darreichungsform erscheint mir außerdem ziemlich unübersichtlich, siehe Bild.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-08-10-htmltest-error-note.avif" alt="Image: Output of a htmltest report in the console. It is completely filled with text. There are different error messages like &amp;#39;name resolution error&amp;#39;, &amp;#39;missing trailing slash&amp;#39; and so on. It is nowhere easy to read but mentions the source path of the issue." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="ausgabe-in-datei-umleiten"&gt;Ausgabe in Datei umleiten&lt;/h3&gt;&#10;&lt;p&gt;Folglich tue ich mich erstmal ziemlich schwer mit diesem Werkzeug. Ich hatte angenommen, dass mir ein paar Links kaputtgegangen sind und nicht mit tausenden Fehlern gerechnet. Also fiel ich dem typischen &lt;a href="https://en.wikipedia.org/wiki/Not_invented_here" target="_blank" rel="noopener noreferrer" class="external-link"&gt;not-invented-here&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Syndrom anheim und programmierte selbst ein Werkzeug in &lt;em&gt;python&lt;/em&gt;, welches sich im Nachhinein als nicht gut genug entpuppte. Kurzgesagt:&lt;/p&gt;&#10;&lt;p&gt;Htmltest ist ein tolles und unglaublich schnelles Werkzeug. Wenn man dessen Ausgabe leserlich macht und richtig deutet, kann man damit ziemlich glücklich werden. Mit ein bisschen Herumprobieren gelange ich zu folgendem Befehl:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;htmltest public/ 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; | ansi2html &amp;gt; htmltest-output.html&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;&#10;&lt;p&gt;Bedeutung: &amp;ldquo;Führe &lt;em&gt;htmltest&lt;/em&gt; im &lt;code&gt;/public/&lt;/code&gt;-Ordner aus. Leite Fehler aus &lt;code&gt;stderr&lt;/code&gt; in die Ausgabe &lt;code&gt;stdout&lt;/code&gt; um. Reiche die Ausgabe an &lt;em&gt;ansi2html&lt;/em&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; weiter. Schreibe dessen Ausgabe in eine Datei namens &lt;code&gt;htmltest-output.html&lt;/code&gt;.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-08-10-htmltest-ansi2html-output.avif" alt="Image: Output of a htmltest report in html after piping through ansi2html, opened with a browser. The lines are formatted properly and have colour coding. Looks neat." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Das sieht doch so schon viel besser aus!&lt;/p&gt;&#10;&lt;h3 id="fehlerkategorien"&gt;Fehlerkategorien&lt;/h3&gt;&#10;&lt;p&gt;Die mit &lt;em&gt;htmltest&lt;/em&gt; gefundene Fehler lassen sich für meine Zwecke in ein paar wenige Kategorien sortieren:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Hugo-intern bedingte Fehler wie durch &lt;code&gt;livereload.js&lt;/code&gt; verursachte &lt;code&gt;target does not exist&lt;/code&gt;-Meldungen.&lt;/li&gt;&#10;&lt;li&gt;Unvollständige Links, die zwar in der Praxis funktionieren, aber &lt;code&gt;href lacks trailing slash&lt;/code&gt;-Fehler erzeugen. Diese Art Fehler machen bei mir den Löwenanteil der berichteten Probleme aus.&lt;/li&gt;&#10;&lt;li&gt;Lookup-Fehler &lt;code&gt;GET &amp;lt;src&amp;gt; [...] failure in name resolution&lt;/code&gt;, bei fehlender Verbindung, unzureichenden Rechten, aktiver Bot-Abwehr oder wenn der Zielserver offline ist&lt;/li&gt;&#10;&lt;li&gt;Harte, durch defekte Links erzeugte &lt;code&gt;target does not exist&lt;/code&gt;-Fehler. Genau diese wollte ich eigentlich beheben.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="das-log-bereinigen"&gt;Das Log bereinigen&lt;/h3&gt;&#10;&lt;p&gt;Wie bekomme ich nun nur noch Fehler der vierten Kategorie angezeigt? Durch ein paar Änderungen in den Werkeugen und die Beseitigung der Trailing-Slash-Fehler.&lt;/p&gt;&#10;&lt;p&gt;Fehler der Kategorie 1, die durch Hugos nützliche &lt;code&gt;livereload&lt;/code&gt;-Funktion hervorgerufen werden, sehen so aus:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt; target does not exist --- en/tags/tools/index.html --&amp;gt; /livereload.js?mindelay=10&amp;amp;v=2&amp;amp;port=1313&amp;amp;path=livereload&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;LiveReload ist diejenige Funktion, die mir ständiges &lt;code&gt;F5&lt;/code&gt;-Tippen im Browserfenster erspart, wenn Hugo eine meiner Änderungen nach dem Speichern der Markdown-Datei baut. Für den Test kann ich es jedoch kurz deaktivieren. Die folgende Lösung reduziert die Anzahl erzeugter Fehler um eins pro html-Seite.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;hugo server --disableLiveReload&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Lookup-Fehler der Kategorie 3 vermeide ich durch einen Fokus auf interne Links worüber ich die vollständige Kontrolle habe. Hierbei hilft mir der Parameter &lt;code&gt;-s, --skip-external&lt;/code&gt; in &lt;em&gt;htmltest&lt;/em&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;htmltest -s public/ 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; | ansi2html &amp;gt; htmltest-output.html&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;✘✘✘ failed in 494.7678ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;446&lt;/span&gt; errors in &lt;span style="color:#ae81ff"&gt;337&lt;/span&gt; documents&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tote externe Links kann ich zu einem späteren Zeitpunkt beheben; nun will ich erstmal die durch die Migration entstandenen Probleme lösen.&lt;/p&gt;&#10;&lt;h2 id="missing-trailing-slash-fehlerbehebung-per-markup"&gt;&amp;ldquo;Missing trailing slash&amp;rdquo;: Fehlerbehebung per Markup&lt;/h2&gt;&#10;&lt;p&gt;Mit den vielen &lt;code&gt;href lacks trailing slash&lt;/code&gt;-Fehlern tue ich mich lange zeit schwer, müsste ich doch an fast jeden Markdown-Link in jedem Artikel ein &lt;code&gt;/&lt;/code&gt; anfügen. Zum Glück finde ich eine Lösung in meinem Markup &lt;code&gt;render-link.html&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{- /* /layouts/_markup/render-link.html */ -}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;.Destination&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;safeURL&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_external&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;or&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasPrefix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;http://&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasPrefix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;https://&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_anchor&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasPrefix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;#&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_asset&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasPrefix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/assets/&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[...]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{- /* Add / to internal paths that have no file extension */ -}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;and&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_external&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_anchor&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_asset&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$parsed&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;urls&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.Parse&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;and&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$parsed&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasSuffix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$parsed&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.Path&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;))&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;eq&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.Ext&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$parsed&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.Path&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;printf&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;%s/&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[...]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;&#10;&lt;p&gt;Bedeutung: Interne Links, die weder auf Überschriften &lt;code&gt;is_anchor&lt;/code&gt; noch Mediendateien &lt;code&gt;is_asset&lt;/code&gt; verweisen, bekommen genau dann ein &lt;code&gt;/&lt;/code&gt; angehangen, wenn sie eine korrekte Syntax aufweisen &lt;code&gt;parsed&lt;/code&gt; und noch keinen Slash am Ende der URL vorweisen können.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Dies löst 95% der Fehlermeldungen durch weniger hässliches &lt;code&gt;html&lt;/code&gt;. Die restlichen 5% hole ich mir durch Anhängen von ein paar Slashes in der Hugo-Konfiguration. Beispiele:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# hugo.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;url&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;/en/&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# /en -&amp;gt; /en/ &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;logoHomeLink&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;/en/&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# /en -&amp;gt; /en/&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Fertig! Übrig bleiben nur noch harte, interne Fehler.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;htmltest -s public/ 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; | ansi2html &amp;gt; htmltest-output.html&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;✘✘✘ failed in 359.28ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;102&lt;/span&gt; errors in &lt;span style="color:#ae81ff"&gt;84&lt;/span&gt; documents&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="manuelles-suchen-und-beheben"&gt;Manuelles Suchen und Beheben&lt;/h2&gt;&#10;&lt;p&gt;Was nun folgt ist reine Fleißarbeit. Ich öffne das Zieldokument (also jenes, auf welches per Link verwiesen wird) und schaue mir an, ob zum Beispiel Überschriftenlinks zwischen verschiedenen Sprachen uneinheitlich sind. Falls ja, füge ich individuelle Anchor-Links an die Überschrift an.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;### Überschrift {#heading3}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Falls in der Zieldatei alles paletti ist, öffne ich zusätzlich alle Quelldateien, die den defekten Link verwenden. Dort korrigiere ich den Link manuell. In den meisten Fällen befindet sich die Zieldatei inzwischen in einem anderen Ordner, oder der Sprachpräfix ist falsch gesetzt.&lt;/p&gt;&#10;&lt;h2 id="das-ergebnis"&gt;Das Ergebnis&lt;/h2&gt;&#10;&lt;p&gt;Nach ein paar Stunden anstrengender und dröger Handrbeit werde ich belohnt:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;htmltest public/ -s 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Skipping the checking of external links.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;htmltest started at 10:51:23 on public&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;========================================================================&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;✔✔✔ passed in 238.970354ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tested &lt;span style="color:#ae81ff"&gt;338&lt;/span&gt; documents&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bald kann ich mit meiner neuen alten Seite online gehen!&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;&lt;a href="https://packages.debian.org/en/sid/colorized-logs" target="_blank" rel="noopener noreferrer" class="external-link"&gt;ansi2html bzw. colorized-logs&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist ein Hilfsprogramm, welches Konsolenausgabe inkl. Formatierung und Textfarben in &lt;code&gt;html&lt;/code&gt; umwandelt.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Hugo Multi-Lang: Interne Permalinks</title><link>https://blog.schallbert.de/hugo-create-language-specific-permalinks/</link><pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/hugo-create-language-specific-permalinks/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-07-07-defaultcontentlanguage-in-hugo-config.avif"&#10; class="post-cover"&#10; alt="Image: An SSH public key randomart image as visual fingerprint for humans to quickly see if public keys match"&#10; title="Hugo Multi-Lang: Interne Permalinks" /&gt;&#10;&lt;p&gt;Wer in Hugo wie ich einen mehrsprachigen Blog baut, könnte folgendes Problem kennen:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Interne Links in Artikeln verschiedener Sprachen verweisen nur auf Seiten in der Standard-Sprache.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h2 id="konfiguration"&gt;Konfiguration&lt;/h2&gt;&#10;&lt;p&gt;Grundlage für das Problem ist die folgende Einstellung:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# hugo.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;defaultContentLanguage&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;de&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;defaultContentLanguageInSubdir&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;disableDefaultLanguageRedirect&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Meine Standardsprache ist Deutsch und zugehörige Artikel werden in das Hauptverzeichnis des Blogs gebaut. Eine Weiterleitung in das Sprachverzeichnis &lt;code&gt;/de&lt;/code&gt; findet nicht statt.&lt;/p&gt;&#10;&lt;p&gt;Die Verzeichnisstruktur lautet:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── content&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── de&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── about.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── announcements&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── legal.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── posts&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   ├── privacy.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   │   └── projects&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   └── en&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── about.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── legal.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── posts&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   ├── privacy.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│   └── projects&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="das-problem-mit-der-standardsprache"&gt;Das Problem mit der Standardsprache&lt;/h2&gt;&#10;&lt;p&gt;Ich halte Artikel durch Nutzung von &lt;a href="https://gohugo.io/content-management/urls/#slug" target="_blank" rel="noopener noreferrer" class="external-link"&gt;slug&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; in meiner Standardsprache unter Adressen erreichbar wie &lt;code&gt;example.com/my-post&lt;/code&gt;, &lt;code&gt;example.com/my-2nd-post/&lt;/code&gt;.&#10;In der Zweitsprache hingegen erscheinen sie als &lt;code&gt;example.com/en/my-post&lt;/code&gt;, &lt;code&gt;example.com/en/my-2nd-post/&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Wenn ich zwei Artikel nun intern verlinken möchte, dann verweist der Link stets auf den Artikel meiner Standardsprache.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# English version of my post&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# example.com/en/my-post&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;slug&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-post&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;Post&amp;#39;s English content&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#ae81ff"&gt;2nd](/my-2nd-post) &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ich erreiche damit statt des gewünschten &lt;code&gt;/en/my-2nd-post&lt;/code&gt; nur die deutsche Version &lt;code&gt;/my-2nd-post&lt;/code&gt;. Das finde ich blöd, denn so müsste ich jeden Link in allen Artikeln manuell anpassen. Leider scheint Hugo dafür keine eingebaute Lösung mitzubringen. Also erstelle ich mir eine Vorschrift, wie Links gerendert werden sollen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{- /* /layouts/_markup/render-link.html */ -}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="lösung-sprachabhängige-link-prefixes"&gt;Lösung: Sprachabhängige Link-Prefixes&lt;/h2&gt;&#10;&lt;p&gt;Dort soll folgendes passieren: Wenn beim Bauen ein relativer Link innerhalb der Seite gefunden wird, so ist genau dann ein Sprachpräfix wie &lt;code&gt;/en&lt;/code&gt; vor diesen Link zu setzen, wenn die Sprache der zu rendernden Seite nicht der Standardsprache entspricht (denn für letztere benötige ich wegen &lt;code&gt;defaultContentLanguageInSubdir: false&lt;/code&gt; keinen Sprachpräfix).&lt;/p&gt;&#10;&lt;h3 id="standardsprache-auslesen"&gt;Standardsprache auslesen&lt;/h3&gt;&#10;&lt;p&gt;Nur wie komme ich an die Standardsprache? Die Parameter &lt;code&gt;defaultContentLanguage&lt;/code&gt; sind in Templates nicht zugänglich. Nach längerer Recherche finde ich eine passende Lösung für das Problem in der &lt;a href="https://discourse.gohugo.io/t/is-there-a-way-to-retrieve-the-value-of-defaultcontentlanguage/9643" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Hugo community&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;: Die Standardsprache ist meist die erste Sprache des gewichtsbasierten Index (welcher die &lt;a href="https://gohugo.io/configuration/languages/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Sprachen nach Reihenfolge&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; erscheinen lässt): &lt;code&gt;index .Page.Site.Home.AllTranslations 0 &lt;/code&gt; Wenn ich diesen nun auslese, ist die &amp;ldquo;erste&amp;rdquo; meine Standardsprache.&lt;/p&gt;&#10;&lt;h3 id="code-für-render-linkhtml"&gt;Code für render-link.html&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{- /* /layouts/_markup/render-link.html */ -}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;.Destination&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;safeURL&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$currentLang&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;.Page.Language.Locale&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_external&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;or&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasPrefix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;http://&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasPrefix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;https://&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_anchor&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasPrefix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;#&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_asset&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasPrefix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/assets/&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_lang_prefixed&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasPrefix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;printf&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/%s/&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$currentLang&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_anchor&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;printf&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;%s%s&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;.Page.RelPermalink&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_external&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;and&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_lang_prefixed&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_asset&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;.Page.Language.IsDefault&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;printf&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/%s%s&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$currentLang&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{- /* Add / to internal paths that have no file extension */ -}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;and&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_external&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_anchor&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_asset&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$parsed&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;urls&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.Parse&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;and&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$parsed&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasSuffix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$parsed&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.Path&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;))&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;eq&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.Ext&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$parsed&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.Path&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;printf&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;%s/&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#75715e"&gt;{{&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;relURL&lt;/span&gt; &lt;span style="color:#75715e"&gt;}}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#75715e"&gt;{{&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_external&lt;/span&gt; &lt;span style="color:#75715e"&gt;}}&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;target&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;_blank&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rel&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;noopener noreferrer&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;external-link&amp;#34;&lt;/span&gt;&lt;span style="color:#75715e"&gt;{{&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt; &lt;span style="color:#75715e"&gt;}}&lt;/span&gt;&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;.Text&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;safeHTML&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_external&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;span&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;external-link-icon&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;aria-hidden&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&amp;gt;↗&amp;lt;/&lt;span style="color:#f92672"&gt;span&lt;/span&gt;&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;a&lt;/span&gt;&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Solange die Standardsprache und der Gewichtsindex übereinstimmen, d.h. die Standardsprache ganz oben auftaucht, funktioniert dies prima.&lt;/p&gt;&#10;&lt;h3 id="zusatzfunktionen"&gt;Zusatzfunktionen&lt;/h3&gt;&#10;&lt;p&gt;Was der Code sonst noch macht:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Prüfung, ob Links nach &amp;ldquo;extern&amp;rdquo;, also von meiner Site weg gehen. Jene bekommen dann ein eigenes Fenster und den Indikator &lt;code&gt;↗&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Prüfung, ob der Link auf eine Überschrift &amp;ldquo;anchor&amp;rdquo; führt. Details dazu siehe unten.&lt;/li&gt;&#10;&lt;li&gt;Prüfung, ob der Link zu einer (Medien-)Datei &amp;ldquo;asset&amp;rdquo; führt. Asset-Links werden nicht modifiziert.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Handelt es sich um einen Überschriftslink, so wird dieser gegebenenfalls auf die vollständige URL ergänzt. Das geschieht nach folgendem Schema: &lt;code&gt;#heading --&amp;gt; /&amp;lt;opt_lang&amp;gt;/&amp;lt;opt_category&amp;gt;/&amp;lt;title&amp;gt;/#heading&lt;/code&gt;&lt;/p&gt;&#10;&lt;h2 id="permalinks"&gt;Richtig auflösende Permalinks auf Überschriften&lt;/h2&gt;&#10;&lt;p&gt;Auch für hochspezielle Permalinks innerhalb einer Seite habe ich eine Lösung gefunden. Nehmen wir an, ich habe verschiedensprachige Überschriften :&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# German&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# content/de/posts/yyyy-mm-dd-hugo-create-language-specific-permalinks.md&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;description&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Mit _markup interne Links richtig routen&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Hugo Multi-Lang: Interne Permalinks&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;slug&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;hugo-create-language-specific-permalinks&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## Richtig auflösende Permalinks auf Überschriften {#permalinks}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;Lorem Ipsum...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# English&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /content/en/posts/yyyy-mm-dd-hugo-create-language-specific-permalinks.md&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;description&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Routing internal links with _markup&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Hugo Multi-Lang: Internal Permalinks&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;slug&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;hugo-create-language-specific-permalinks&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## Correct Permalinks to headings {#permalinks}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;Lorem Ipsum...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dann sollte ein Klick auf &lt;a href="https://blog.schallbert.de/hugo-create-language-specific-permalinks/#permalinks"&gt;diesen Link zurück zur Überschrift #permalinks&lt;/a&gt; sprachunabhängig wieder zurück zur Überschrift führen. Schlüssel für diese Lösung ist das Einführen einer Überschriftsdefinition (&lt;a href="https://www.markdownlang.com/cheatsheet/headings.html#heading-ids-extended-syntax" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Heading-ID&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) in Markdown, in diesem Fall &lt;code&gt;{#permalinks}&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="funktioniert-so-noch-nicht"&gt;Funktioniert so noch nicht.&lt;/h3&gt;&#10;&lt;p&gt;Weil &lt;code&gt;#permalinks&lt;/code&gt; auf &lt;code&gt;&amp;lt;blog's baseurl&amp;gt;/#permalinks&lt;/code&gt; verweist und somit der Link auf die aktuelle Seite fehlt. Doch auch das bekommen wir hin: Ich erweitere den Link-Renderer um ein paar Zeilen Code und gebe die aktuelle Seiten-URL als Präfix hinzu.&lt;/p&gt;&#10;&lt;h3 id="code-für-render-linkhtml-mit-unterstützung-für-überschriften"&gt;Code für render-link.html mit Unterstützung für Überschriften&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_anchor&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;strings&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.HasPrefix&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;#&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$is_anchor&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;printf&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;%s%s&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;.Page.RelPermalink&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$url&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;{{-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt; &lt;span style="color:#75715e"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nun wird &lt;code&gt;(#permalinks)&lt;/code&gt;erweitert zu &lt;code&gt;/hugo-create-language-specific-permalinks/#permalinks&lt;/code&gt;, sodass der Link endlich zum Ziel führt.&lt;/p&gt;&#10;</description></item><item><title>Scheitern von SSH-Verbindungen</title><link>https://blog.schallbert.de/ssh-typical-issues/</link><pubDate>Fri, 12 Jun 2026 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/ssh-typical-issues/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-06-12-ssh-errors-thumb.avif"&#10; class="post-cover"&#10; alt="Image: A list of different SSH error messages. Some very common, but a few have rare causes."&#10; title="Scheitern von SSH-Verbindungen" /&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-06-12-ssh-icon.svg" alt="Image: terminal logo image from Aegis simple image pack, framing the topic of this post." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;In diesem kurzen Artikel geht es um Fehler beim Aufbau einer SSH-Verbindung. Egal ob andere Rechner, Server oder Dienste wie Versionsverwaltung - die Fehlermeldungen und zugehörige Lösungen sind meist gleich. Weiterlesen lohnt sich allerdings nur, wenn gängige Hilfeseiten wie &lt;a href="https://docs.github.com/en/authentication/troubleshooting-ssh" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Githubs troubleshooting-ssh&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; nicht die erwünschte Lösung brachte.&lt;/p&gt;&#10;&lt;h2 id="problem-no-route-to-host"&gt;Problem: &amp;ldquo;No Route To Host&amp;rdquo;&lt;/h2&gt;&#10;&lt;p&gt;Die Konsolenausgabe zeigt (im lokalen Netz):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;➜ blog git:&lt;span style="color:#f92672"&gt;(&lt;/span&gt;article&lt;span style="color:#f92672"&gt;)&lt;/span&gt; ✗ ssh 192.168.0.27&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh: connect to host 192.168.0.27 port 22: No route to host&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Oder (in einem entfernten Netz)&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;➜ blog git:&lt;span style="color:#f92672"&gt;(&lt;/span&gt;article&lt;span style="color:#f92672"&gt;)&lt;/span&gt; ✗ ssh 1.2.3.4&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh: connect to host 1.2.3.4 port 22: Network is unreachable&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ursache ist sehr wahrscheinlich eine falsch eingegebene IP oder eine fehlgeschlagene DNS-Namensauflösung. Im lokalen Netz ist ein realistischer Grund, dass der Teilnehmer abgeschaltet ist und der Router ihn daher nicht erreichen kann. Abhilfe schafft meist eine Korrektur der Zieladresse, Einschalten des Ziels oder Update der Routing-Tabelle.&lt;/p&gt;&#10;&lt;h2 id="problem-connection-refused"&gt;Problem: Connection Refused&lt;/h2&gt;&#10;&lt;p&gt;Konsolenausgabe:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;➜ blog git:&lt;span style="color:#f92672"&gt;(&lt;/span&gt;article&lt;span style="color:#f92672"&gt;)&lt;/span&gt; ✗ git push gitea&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh: connect to host git.schallbert.de port 22: Connection refused&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fatal: Could not read from remote repository.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hier kann nicht mal eine Verbindung zum Ziel hergestellt werden. Man ist also &amp;ldquo;vor&amp;rdquo; der Authentisierung schon gescheitert. Meiner Erfahrung nach werden solche Fehler aus drei Gründen erzeugt:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Der Host ist noch nicht bereit. Er bootet beispielsweise gerade (Netzwerkkarte ist an) aber der SSH-Dienst ist noch nicht hochgefahren. Hier hilft meist: abwarten.&lt;/li&gt;&#10;&lt;li&gt;Eine Firewall, ein Rate Limiter, oder Dienste wie &lt;em&gt;fail2ban&lt;/em&gt; weisen die Verbindung ab (&lt;code&gt;REJECT&lt;/code&gt;). Die Gründe hierfür sind meist zu viele erfolglose Anmeldeversuche oder eine fehlgeleitete Bot-Erkennung. In seltenen Fällen kann es aber auch sein, dass die eigene IP-Adresse auf einer &amp;ldquo;deny-list&amp;rdquo; auftaucht, die von den Zielen eingelesen wurde. Auch hier empfiehlt sich: warten.&lt;/li&gt;&#10;&lt;li&gt;Der angesprochene Dienst erwartet einen anderen Port als den SSH-Standard &lt;code&gt;Port 22&lt;/code&gt;. Somit stimmen User/Ziel-Adresse/Port nicht überein.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Das Port-Problem lässt sich leicht lösen.&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-06-12-gitea-server-configuration-ssh-port.avif" alt="Image: Gitea showing SSH port IDs under /admin/config/Server_Configuration/SSH_Configuration, Port:222, Listen Port: 22" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Herausfinden des &amp;ldquo;Listen Port&amp;rdquo; auf dem Host (Server Configuration / SSH). Auf dem Bild ist dargestellt, wie die Einstellung auf meiner Gitea-Instanz aussieht.&#10;Alternativ genügt meist ein Blick in die Konfigurationsdatei des entsprechenden Services.&lt;/li&gt;&#10;&lt;li&gt;Die SSH-Konfiguration unter &lt;code&gt;~./ssh/config&lt;/code&gt; mit einer konkreten Port-Nummer versehen. Beispiel:&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /.ssh/config&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Host gitserver&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Hostname git.schallbert.de&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Port &lt;span style="color:#ae81ff"&gt;222&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;...&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="problem-too-many-authentication-failures"&gt;Problem: Too Many Authentication Failures&lt;/h2&gt;&#10;&lt;p&gt;Konsolenausgabe:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;➜ blog git:&lt;span style="color:#f92672"&gt;(&lt;/span&gt;article&lt;span style="color:#f92672"&gt;)&lt;/span&gt; ✗ git pull&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Received disconnect from &amp;lt;ipAddress&amp;gt; port &amp;lt;portId&amp;gt;: Too many authentication failures&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Wir haben nun also eine Verbindung zum Server 😁, aber der mag uns nicht reinlassen 😨. Immerhin wird mitgeteilt, warum: Er meint, wir hätten zu viele Schlüssel an seiner Haustür ausprobiert.&lt;/p&gt;&#10;&lt;h3 id="server-anmeldelimit-prüfen"&gt;Server-Anmeldelimit prüfen&lt;/h3&gt;&#10;&lt;p&gt;Dieser Fehler tritt auf, wenn das Server-Limit der Anmeldeversuche gerissen wird. Der Wert beträgt beim Service &lt;code&gt;sshd&lt;/code&gt; beispielsweise standardmäßig &lt;a href="https://www.baeldung.com/linux/too-many-authentication-failures-error" target="_blank" rel="noopener noreferrer" class="external-link"&gt;MaxAuthTries = 6&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Hat man viele Schlüssel für mehrere Verbindungen abgespeichert oder verwendet wie ich neuerdings &lt;a href="https://blog.schallbert.de/hardware-security-keys-yubikey/"&gt;Hardware Security Keys&lt;/a&gt; und legt Zweitschlüssel an (also &lt;code&gt;ssh-keys.count*2&lt;/code&gt;), wird dieser Wert schnell überschritten.&lt;/p&gt;&#10;&lt;p&gt;Fies bei diesem Fehler ist außerdem, dass er nur bei bestimmten Services &amp;ldquo;unten&amp;rdquo; in des Agents Schlüsselbund auftritt: unter den ersten sechs dürfen ja fünf Schlüssel falsch sein. Zudem schützt einen nicht, wenn man die Schlüssel in der &lt;code&gt;~./ssh/config&lt;/code&gt; korrekt hinterlegt hat. Der Agent nimmt stets alle Schlüssel zum Probieren mit, wenn man es ihm nicht explizit verbietet.&lt;/p&gt;&#10;&lt;p&gt;Das Verbot erteilen wir per &lt;code&gt;IdentitiesOnly yes&lt;/code&gt;. Hiermit wird dem Agent klar gesagt, dass er nur die für diesen Host explizit angegebenen Identities (Also Schlüsseldateien oder Nutzername/Passwort) verwenden soll.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /.ssh/config&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host gitserver&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Hostname git.schallbert.de&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Port &lt;span style="color:#ae81ff"&gt;222&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; User &amp;lt;user&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; PreferredAuthentications publickey&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; IdentityFile &amp;lt;path_to_private_key1&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Identityfile &amp;lt;path_to_private_key2&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; IdentitiesOnly yes&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="git-config-überprüfen"&gt;Git-Config überprüfen&lt;/h3&gt;&#10;&lt;p&gt;Diesen Fehler bekommt man auch, wenn die Git-Konfiguration des Repository unter &lt;code&gt;&amp;lt;reponame&amp;gt;/.git/config&lt;/code&gt; nicht mit dem in der SSH-Konfiguration &lt;code&gt;~/.ssh/config&lt;/code&gt; hinterlegten Datensatz für Server und Repository übereinstimmt. Ändert man die SSH-Konfiguration, so sind alle auf diesem Server bereitgehaltenen Repo-Konfigurationen ebenfalls zu ändern.&lt;/p&gt;&#10;&lt;p&gt;Beispiel:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# &amp;lt;reponame&amp;gt;/.git/config&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;remote &lt;span style="color:#e6db74"&gt;&amp;#34;origin&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ssh://git@git.schallbert.de:222/schallbert/&amp;lt;reponame&amp;gt;.git&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;fetch &lt;span style="color:#f92672"&gt;=&lt;/span&gt; +refs/heads/*:refs/remotes/origin/*&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;branch &lt;span style="color:#e6db74"&gt;&amp;#34;main&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;remote &lt;span style="color:#f92672"&gt;=&lt;/span&gt; origin&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;merge &lt;span style="color:#f92672"&gt;=&lt;/span&gt; refs/heads/main&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;vscode-merge-base &lt;span style="color:#f92672"&gt;=&lt;/span&gt; origin/main&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In diesem Falle wird die &lt;code&gt;gitserver&lt;/code&gt;-Konfiguration gar nicht verwendet, sondern sich direkt einwählt. Dies führt dazu, dass der Agent kein &lt;code&gt;IdentitiesOnly yes&lt;/code&gt; Flag mitgeliefert bekommt und er Schlüssel durchprobiert, bis der Server abwinkt. Lösung:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;remote &lt;span style="color:#e6db74"&gt;&amp;#34;origin&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; git@gitserver:schallbert/&amp;lt;reponame&amp;gt;.git&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;fetch &lt;span style="color:#f92672"&gt;=&lt;/span&gt; +refs/heads/*:refs/remotes/origin/*&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="problem-permission-denied-publickeypassword"&gt;Problem: Permission Denied (publickey,password)&lt;/h2&gt;&#10;&lt;p&gt;Konsolenausgabe:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;➜ blog git:&lt;span style="color:#f92672"&gt;(&lt;/span&gt;article&lt;span style="color:#f92672"&gt;)&lt;/span&gt; ✗ ssh -T git@gitserver&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git@git.schallbert.de: Permission denied &lt;span style="color:#f92672"&gt;(&lt;/span&gt;publickey,password&lt;span style="color:#f92672"&gt;)&lt;/span&gt;.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dieses Problem kann leider viele verschiedene Ursachen haben. Ein paar räumt Githubs &lt;a href="https://docs.github.com/en/authentication/troubleshooting-ssh/error-permission-denied-publickey" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Anleitung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; bereits aus dem Weg, darunter:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;sudo&lt;/code&gt; verwendet&lt;/li&gt;&#10;&lt;li&gt;Falscher Server&lt;/li&gt;&#10;&lt;li&gt;nicht den &lt;code&gt;git&lt;/code&gt;-user verwendet&lt;/li&gt;&#10;&lt;li&gt;Falschen Schlüssel verwendet (client)&lt;/li&gt;&#10;&lt;li&gt;Schlüssel nicht hinterlegt (server)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Die Ausgabe bei falschem Username sieht so aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;➜ blog git:&lt;span style="color:#f92672"&gt;(&lt;/span&gt;article&lt;span style="color:#f92672"&gt;)&lt;/span&gt; ✗ ssh -T mit@gitserver&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mit@git.schallbert.de: Permission denied &lt;span style="color:#f92672"&gt;(&lt;/span&gt;publickey&lt;span style="color:#f92672"&gt;)&lt;/span&gt;.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Eine weitere Ursache kann eine fehlende Abstimmung zwischen der Konfigurationsdatei &lt;code&gt;~./ssh/config&lt;/code&gt; und dem im &lt;em&gt;git client&lt;/em&gt; hinterlegten Remote-Adresse liegen. Sind diese nicht deckungsgleich, wird eine eventuell bestehende Konfiguration gar nicht erst benutzt.&lt;/p&gt;&#10;&lt;p&gt;Eine Überprüfung kann man mit &lt;code&gt;git remote -v&lt;/code&gt; durchführen.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;➜ blog git:&lt;span style="color:#f92672"&gt;(&lt;/span&gt;article&lt;span style="color:#f92672"&gt;)&lt;/span&gt; ✗ git remote -v&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;origin&#9;ssh://git@git.schallbert.de:222/schallbert/blog.git &lt;span style="color:#f92672"&gt;(&lt;/span&gt;fetch&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;origin&#9;ssh://git@git.schallbert.de:222/schallbert/blog.git &lt;span style="color:#f92672"&gt;(&lt;/span&gt;push&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Korrekt wäre mit der verbesserten Konfigurationsdatei (Ports eindeutig gesetzt, Identity Files definiert und auf die Angegebenen beschränkt):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Update link to server&amp;#39;s git repository, use host alias from config file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;➜ blog git:&lt;span style="color:#f92672"&gt;(&lt;/span&gt;article&lt;span style="color:#f92672"&gt;)&lt;/span&gt; ✗ git remote update origin git@gitserver:schallbert/blog.git&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Now check if the update was effective&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;➜ blog git:&lt;span style="color:#f92672"&gt;(&lt;/span&gt;article&lt;span style="color:#f92672"&gt;)&lt;/span&gt; ✗ git remote -v&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;origin&#9;git@gitserver:schallbert/blog.git &lt;span style="color:#f92672"&gt;(&lt;/span&gt;fetch&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;origin&#9;git@gitserver:schallbert/blog.git &lt;span style="color:#f92672"&gt;(&lt;/span&gt;push&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="problem-agent-refused-operation"&gt;Problem: Agent Refused Operation&lt;/h3&gt;&#10;&lt;p&gt;Konsolenausgabe:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;➜ blog git:&lt;span style="color:#f92672"&gt;(&lt;/span&gt;local-setup&lt;span style="color:#f92672"&gt;)&lt;/span&gt; ✗ git push --set-upstream origin local-setup &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sign_and_send_pubkey: signing failed &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; ED25519-SK &amp;lt;path-to-private-key&amp;gt; from agent: agent refused operation&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Diesen Fehler sehe ich erst, seitdem ich &lt;a href="https://blog.schallbert.de/hardware-security-key-add-ssh/"&gt;Hardware Security Keys für ssh verwende&lt;/a&gt;. Aus meiner Sicht kann er zwei Ursachen haben. Beide sind zum Glück leicht zu beheben:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Der Agent kennt den Schlüssel nicht. Der Befehl &lt;code&gt;ssh-add&lt;/code&gt; löst das Problem. Prüfung per &lt;code&gt;ssh-add -l&lt;/code&gt; | grep &amp;ldquo;&amp;lt;your-key&amp;rsquo;s-comment&amp;gt;&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;Der angeforderte Schlüssel ist nicht da. Kann bei HSK und HSM passieren, falls sie nicht eingesteckt sind, nicht richtig auf dem NFC-Lesegerät platziert sind oder das HSM im Netzwerk nicht verfügbar ist. Oder wenn manuell im Verzeichnis &lt;code&gt;~./ssh/config&lt;/code&gt; gearbeitet wurde und der Schlüssel nun umbenannt oder gelöscht ist.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;</description></item><item><title>Hardware Security Key: SSH einrichten</title><link>https://blog.schallbert.de/hardware-security-key-add-ssh/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/hardware-security-key-add-ssh/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-05-03-ssh-randomart_thumb.avif"&#10; class="post-cover"&#10; alt="Image: An SSH public key randomart image as visual fingerprint for humans to quickly see if public keys match"&#10; title="Hardware Security Key: SSH einrichten" /&gt;&#10;&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;&#10;&lt;p&gt;In meinem Post über &lt;a href="https://blog.schallbert.de/hardware-security-keys-yubikey/"&gt;Hardware Security Keys&lt;/a&gt; schrieb ich, dass ich möglichst viele meiner Verbindungen passwortlos mit meinem neuen HSK absichern möchte. Ich verwende &lt;em&gt;SSH&lt;/em&gt;-Verbindungen für verschiedene Dienste und Zwecke: Ich greife auf meinen &lt;a href="https://en.wikipedia.org/wiki/Virtual_private_server" target="_blank" rel="noopener noreferrer" class="external-link"&gt;VPS&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zu, benutze Dienste wie Github, Gitea sowie meine eigene &lt;a href="https://blog.schallbert.de/projects/move-blog-to-own-server/"&gt;Gitea-Instanz&lt;/a&gt; und lasse die Erzeugung von Backups im lokalen Netz über &lt;a href="https://linuxize.com/post/how-to-use-scp-command-to-securely-transfer-files/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;code&gt;scp&lt;/code&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; laufen, welches das SSH-Protokoll verwendet.&lt;/p&gt;&#10;&lt;h2 id="ssh-zugriff-für-hardware-security-keys-einrichten"&gt;SSH-Zugriff für Hardware Security Keys einrichten&lt;/h2&gt;&#10;&lt;p&gt;HSKs wie mein Yubikey können private Schlüssel für SecureShell-Zugriffe abspeichern. Somit muss man sie nicht mehr lokal auf jedem Endgerät ablegen (das bleibt aber konfigurierbar, siehe unten). Zudem sind sie gegenüber der Verwahrung auf einem Rechner besser gegen unbefugte Zugriffe geschützt.&lt;/p&gt;&#10;&lt;p&gt;Also probiere ich aus, meinen Github-SSH-Zugriff auf HSK umzustellen und folge &lt;a href="https://www.yubico.com/blog/github-now-supports-ssh-security-keys/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Yubicos Anleitung für passwordlose Authentisierung für Github&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Das Verfahren zur Erzeugung von SSH-Schlüsseln nach FIDO unterscheidet sich für verschiedene Schlüsselhersteller nicht. Ich verlinke ebenfalls die &lt;a href="https://docs.nitrokey.com/nitrokeys/features/fido2/ssh" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Nitrokey-Anleitung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; als Referenz.&lt;/p&gt;&#10;&lt;h2 id="was-so-nicht-funktioniert"&gt;Was so nicht funktioniert&lt;/h2&gt;&#10;&lt;p&gt;Doch mit den Empfehlungen von Yubico selbst komme ich auf meinem Betriebssystem und der Standardinstallation von &lt;code&gt;openssh &lt;/code&gt;nicht zum Ziel. Das aus der Anleitung zusammengebaute Kommando lautet:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# ssh-keygen -t ed25519-sk -O resident -O verify-required -O application&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ssh:github -O user&lt;span style="color:#f92672"&gt;=&lt;/span&gt;schallbert -C &lt;span style="color:#e6db74"&gt;&amp;#34;schallbert@github.com&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nach Erstellung muss man den öffentliche Schlüssel (Dateiendung &lt;code&gt;.pub&lt;/code&gt;) in der Web-Anwendung des jeweiligen Dienstes im Menüpunkt SSH-Zugang hinterlegen.&lt;/p&gt;&#10;&lt;h3 id="bedeutung-des-kommandos-und-der-wichtigsten-optionen"&gt;Bedeutung des Kommandos und der wichtigsten Optionen&lt;/h3&gt;&#10;&lt;p&gt;Dieser Befehl&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; sagt der Anwendung &lt;a href="https://www.man7.org/linux/man-pages/man1/ssh-keygen.1.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;ssh-keygen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, dass sie mir einen Schlüssel des Typs &lt;code&gt;-t ed25519-sk&lt;/code&gt; erzeugen soll. Die Endung &lt;code&gt;-sk&lt;/code&gt; sagt aus, dass der Schlüssel auf einem Security Key - in meinem Fall dem Yubikey - gespeichert werden soll.&lt;/p&gt;&#10;&lt;p&gt;Um das Flag &lt;code&gt;-O resident&lt;/code&gt; zu verstehen muss man wissen, dass FIDO Authenticator-Schlüssel wie mit &lt;code&gt;-sk&lt;/code&gt; erzeugt aus zwei Teilen bestehen: Ein &lt;code&gt;key handle&lt;/code&gt; Teil, welcher auf der Festplatte des Rechners verbleibt und der private Schlüssel selbst, welcher &lt;a href="https://www.man7.org/linux/man-pages/man1/ssh-keygen.1.html#FIDO_AUTHENTICATOR" target="_blank" rel="noopener noreferrer" class="external-link"&gt;nur auf dem HSK existiert&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Nur zusammen können sie Authentisierungsanfragen korrekt beantworten. Das Flag selbst weist jedoch an, beide Schlüsselteile auf den Yubikey zu schreiben. Das erleichtert die Verwendung mehrerer Geräte mit demselben Schlüssel: Der &lt;code&gt;key handle&lt;/code&gt;-Teil kann vom Yubikey ausgelesen und auf anderen Endgeräten abgelegt werden. Auf der anderen Seite jedoch genügt nun das Verlorengehen des Yubikey allein, um möglichen Angreifern SSH-Zugriff zu geben.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;-f filename&lt;/code&gt; legt Namen für die Schlüsseldateien fest. Macht besonders dann Sinn, wenn man mehrere SSH-Verbindungen oder HSM verwalten muss. Ich habe mir eine Konvention erstellt, um nicht durcheinander zu kommen. Diese orientiert sich stark an der Schreibweise des Schlüssel-Auslesens von einem HSK (&lt;code&gt;ssh-keygen -K&lt;/code&gt;).&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-f &amp;lt;keyType&amp;gt;_&amp;lt;protocol&amp;gt;:&amp;lt;service&amp;gt;_&amp;lt;user&amp;gt;_&amp;lt;device&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Als konkretes Beispiel würde mein Dateiname hier wie folgt lauten:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-f ed25519-sk_ssh:github_schallbert_yubikey-main&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;⚠️ Obacht bei Nutzung von Windows: Dort ist der Doppelpunkt &lt;code&gt;:&lt;/code&gt; nicht als Zeichen in Dateinamen erlaubt. Er wird durch den Unterstrich &lt;code&gt;_&lt;/code&gt; ersetzt, was in der &lt;code&gt;config&lt;/code&gt; entsprechend abgebildet werden muss (&lt;code&gt;strg+h&lt;/code&gt; -&amp;gt; &lt;code&gt;:&lt;/code&gt; ersetzen mit &lt;code&gt;_&lt;/code&gt;)&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;-C schallbert@github.com&lt;/code&gt; fügt der Schlüsseldatei selbst einen Kommentar hinzu, zum Beispiel eine Kontaktadresse. Somit bekommt der Schlüssel ein Label, was die Handhabung unter mehreren vereinfacht.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;-O application=ssh:github&lt;/code&gt; dient nur der sauberen Auflistung im Authenticator-Programm und sorgt dafür, dass ich Schlüssel nicht miteinander verwechseln kann. Ansonsten steht dort nämlich nur &lt;code&gt;ssh:keygen&lt;/code&gt; und ich kann nicht sehen, für welche Services ich Schlüssel hinterlegt habe. Außerdem hilfreich bei der Verwendung von &lt;code&gt;-resident&lt;/code&gt;, da bei der Extraktion der Schlüssel mit &lt;code&gt;ssh-keygen -K&lt;/code&gt; Unterscheidbarkeit gegeben bleibt.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;-O user=schallbert&lt;/code&gt; hat für mich keinen praktischen Nutzen, da SSH dieses Flag nicht interessiert und der Yubikey Authenticator meinen Nutzernamen anders als erwartet nicht anzeigt.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;-O no-touch-required&lt;/code&gt; vermeidet, dass der Hardware Security Key bei jeder Verwendung des Schlüssels berührt werden muss. Nützlich bei sehr häufiger Verwendung, allerdings Sicherheitsverlust durch Automatisierbarkeit.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;-O verify-required&lt;/code&gt; sagt aus, dass die PIN des HSM abzufragen ist, bevor der Schlüssel genutzt werden kann. Eigentlich als zusätzliche Sicherheit gedacht, bekomme ich damit in meiner Shell jedoch Probleme. Und zwar wird die PIN einmal abgefragt, erzeugt aber vom &lt;code&gt;ssh-agent&lt;/code&gt; nicht jedes mal eine Abfrage in meiner Konsole. Sobald ich in einer anderen Session versuche mich per SSH zu verbinden, erhalte ich eine Fehlermeldung &lt;code&gt;agent refused operation&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# ssh -T git@github.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sign_and_send_pubkey: signing failed &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; ED25519-SK &lt;span style="color:#e6db74"&gt;&amp;#34;schallbert@github.com&amp;#34;&lt;/span&gt; from agent: agent refused operation&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;root@opnsense: Permission denied &lt;span style="color:#f92672"&gt;(&lt;/span&gt;publickey&lt;span style="color:#f92672"&gt;)&lt;/span&gt;.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="woran-hat-es-gelegen"&gt;Woran hat es gelegen?&lt;/h2&gt;&#10;&lt;p&gt;Nach langer, frustrierender Fehlersuche (Was mache ich falsch? Funktioniert es mit dem Zweitschlüssel? Warum geht es beim ersten mal, danach aber nicht mehr? Was ist, wenn ich die Schlüssel erneut erstelle? Was passiert an anderen Rechnern? Was meint &amp;ldquo;KI&amp;rdquo; dazu? - Letzteres kostete bestimmt eine Stunde und ließ mich komplett auf der Stelle treten) probiere ich es mit einem anderen Kommando. Das funktioniert auf Anhieb.&lt;/p&gt;&#10;&lt;h2 id="was-hingegen-funktioniert"&gt;Was hingegen funktioniert&lt;/h2&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# ssh-keygen -t ed25519-sk -O application&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ssh:github -C &lt;span style="color:#e6db74"&gt;&amp;#34;schallbert@github.com&amp;#34;&lt;/span&gt; -f ed25519-sk_ssh:github_schallbert_yubikey-main&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lasse ich &lt;code&gt;verify-required&lt;/code&gt; weg, kann ich mich erfolgreich anmelden und der SSH-agent läuft sauber durch.&lt;/p&gt;&#10;&lt;h3 id="was-verify-required-benötigt"&gt;Was &lt;code&gt;verify-required&lt;/code&gt; benötigt&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://superuser.com/questions/1804740/yubikey-ssh-authentication-fails-with-signing-failed-for-ecdsa-sk" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Dieser Post im Forum superuser&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; bringt für mein Fehlerbild Licht ins Dunkel. Die Terminal-Session mit der ich das SSH-Schlüsselpaar erstelle speichert die per &lt;code&gt;verify-required&lt;/code&gt; Flag erzwungene Pin-Eingabe zwischen. Daher funktioniert der Login erst einmal. Logge ich mich später oder in einem anderen Fenster ein, wie ich es oben getan habe um z.B. &lt;code&gt;git pull&lt;/code&gt; in einem meiner Repos zu prüfen, fragt der SSH-Agent meine PIN nicht ab.&lt;/p&gt;&#10;&lt;p&gt;Das liegt wohl daran, dass der SSH-Agent selbst keine Prompts an das anfragende Programm ausgeben kann. Abhilfe schafft ein Untermodul von Openssh: &lt;a href="https://packages.fedoraproject.org/pkgs/openssh/openssh-askpass/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;openssh-askpass&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Dieses kann man dem Agent übergeben, welcher dann die PIN-Abfrage über &lt;em&gt;askpass&lt;/em&gt; an den User übermittelt.&lt;/p&gt;&#10;&lt;h3 id="installation-von-ssh-askpass"&gt;Installation von ssh-askpass&lt;/h3&gt;&#10;&lt;p&gt;Mein Betriebssystem hatte &lt;em&gt;ssh-askpass&lt;/em&gt; nicht installiert, daher hole ich das hier nach. Es verwendet den Paketmanager &lt;code&gt;DNF&lt;/code&gt; &amp;ldquo;Dandified YUM&amp;rdquo; statt &lt;em&gt;apt&lt;/em&gt; oder anderer gängiger Manager. Dabei ist die Namensgebung meiner Meinung nach nicht 100% einheitlich, spreche ich den ssh-Programme doch im Terminal mit &lt;em&gt;ssh-agent&lt;/em&gt; an, muss jedoch &lt;em&gt;openssh-agent&lt;/em&gt; tippen für die Installation.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# verify-required needs openssh subpackage through Fedora&amp;#39;s package manager&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# sudo dnf install openssh-askpass&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Installing:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; openssh-askpass&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;...&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Complete!&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nun will ich dem Agent mitteilen, dass er &lt;em&gt;ssh-askpass&lt;/em&gt; zum Erfragen der PIN ansprechen kann. Dafür mus ich wissen, in welchem Pfad sich das Submodul befindet.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# which openssh-askpass&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/bin/which: no openssh-askpass &lt;span style="color:#f92672"&gt;[&lt;/span&gt;...&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Komisch, aber ich hatte es doch installiert! Frage ich mal den Paketmanager:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# dnf repoquery --list openssh-askpass&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;...&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/libexec/openssh/ssh-askpass&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aha, openssh versteckt sich im Ordner &lt;code&gt;libexec&lt;/code&gt;. Na dann kann ich das Modul jetzt dem Agent bekanntgeben.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# eval &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;ssh-agent -s; SSH_ASKPASS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/usr/libexec/openssh/ssh-askpass&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Agent pid &lt;span style="color:#ae81ff"&gt;16747&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-05-03-ssh-askpass-prompt.avif" alt="Image: Testing the ssh connection to Github with ssh -T and getting prompted for HSK device pin" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich prüfe die Verbindung zu Github und bekomme wie gewünscht einen Dialog angezeigt, der meine PIN abfragt. Anschließend muss ich den HSK noch berühren und bekomme dann die Erfolgsmeldung. Damit das alles auch in einem neuen Terminal und nach Neustart des Rechners funktioniert, füge ich eine Referenz auf den Schlüssel in meine Konfigurationsdatei ein.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ~/.ssh/config&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host github&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; User git&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Hostname github.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; PreferredAuthentications publickey&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; IdentityFile ~/.ssh/ed25519-sk_ssh:github_schallbert_yubikey-main&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Um auf Nummer sicher zu gehen, den Agenten neustarten und neue Terminalsessions erstellen. Sollten Zugangsdaten im Cache gewesen sein, ist das hiermit zurückgesetzt. Anschließend erneut die SSH-Verbindung testen.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; schallbert@machine:~# eval &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;ssh-agent -k&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; eval &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;ssh-agent -s&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="nervig-gnome-popup-allow-inhibiting-shortcuts"&gt;Nervig: Gnome-Popup &amp;ldquo;Allow inhibiting shortcuts&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-05-03-gnome-inhibit-shortcuts-prompt.avif" alt="Image: Gnome popup *Allow inhibiting shortcuts*" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Einziger Wehrmutstropfen ist, dass ich jetzt beim Verbinden mit SSH ein zusätzliches Popup bekomme. Das wird durch die grafische Nutzeroberfläche &lt;em&gt;gnome&lt;/em&gt; ausgelöst. Leider finde ich keine Option, die zugehörige Einstellung permanent speichern. Somit wird es bei jeder Verbindung mit &lt;code&gt;verify-required&lt;/code&gt; angezeigt. Bei Gnome wird ist das Problem bereits &lt;a href="https://gitlab.gnome.org/GNOME/gnome-connections/-/work_items/85" target="_blank" rel="noopener noreferrer" class="external-link"&gt;für andere Anwendungen bekannt&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, anscheinend aber noch nicht abgestellt.&lt;/p&gt;&#10;&lt;h2 id="übertragung-auf-windows-systeme"&gt;Übertragung auf Windows-Systeme&lt;/h2&gt;&#10;&lt;p&gt;Ich habe meine Schlüssel ja mit der Option &lt;code&gt;-O resident&lt;/code&gt; erzeugt. Da liegt es nahe, die Extraktion des &lt;code&gt;key-handle&lt;/code&gt; mal auf einem komplett anderen System zu testen: &lt;em&gt;Windows&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Ich öffne die Kommandozeile und tippe&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@windows-machine:~# ssh-keygen -K&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Enter PIN &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; authenticator:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;You may need to touch your authenticator to authorize key download.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Provider &lt;span style="color:#e6db74"&gt;&amp;#34;internal&amp;#34;&lt;/span&gt; returned failure -1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Unable to load resident keys: invalid format&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Invalid format? Im Netz steht öfters geschrieben, dass die Schlüssel manchmal am Ende noch ein &lt;a href="https://stackoverflow.com/questions/47859437/windows-10-openssh-key-invalid-format#68593742" target="_blank" rel="noopener noreferrer" class="external-link"&gt;LineFeed-Zeichen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; benötigen um richtig erkannt zu werden. Ohne Download vom HSK kann ich sie aber nicht modifizieren. Also übertrage ich öffentlichen Schlüssel und key-handle manuell auf den Rechner und probiere testweise das &lt;code&gt;git pull&lt;/code&gt;-Kommando aus. Und siehe da: Es klappt!&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-05-03-windows-fido-key-ssh-pin-prompt.avif" alt="Image: FIDO ssh connection test on a Windows machine: OS prompts for PIN entry. It works!" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Möchte man also die Schlüssel mit verschiedenen Betriebssystemfamilien verwenden, nutzt die Option &lt;code&gt;-resident&lt;/code&gt; anscheinend nichts. Der Befehl &lt;code&gt;ssh-keygen -K&lt;/code&gt; ist nicht granular steuerbar: Er lädt alle auf dem HSK befindlichen resident keys herunter.&lt;/p&gt;&#10;&lt;h2 id="vps-vorsicht-bei-schlüssel-ersatz"&gt;VPS: Vorsicht bei Schlüssel-Ersatz&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-05-03-hetzner-key-add-irrelevant-for-existing-servers.avif" alt="Image: Hetzner telling me that new SSH keys will not propagate to existing resources (VPS). Important to know!" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Noch ein Hinweis aus eigener Erfahrung: Hat man bereits SSH-Schlüssel hinterlegt bei einem Dienstanbieter, der selber Ressourcen bereitstellt (In meinem Fall einen Server als VPS) so werden neue SSH-Schlüssel meist nicht über das Web-Frontend an diese Ressource weitergereicht.&lt;/p&gt;&#10;&lt;p&gt;Die &amp;ldquo;alten&amp;rdquo; Schlüssel werden folglich weiterhin benötigt und die Neuen nicht verwendet, obwohl sie in der Web-App abgelegt sind!&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Die neuen Schlüssel müssen dem Ziel manuell bekanntgemacht werden.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Das führe ich der &lt;a href="https://community.hetzner.com/tutorials/howto-ssh-key/de#schritt-4---ssh-key-auf-server-hinterlegen" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Anleitung von meinem Hoster Hetzner&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; folgend mit dem Befehl &lt;code&gt;ssh-copy-id&lt;/code&gt; aus.&lt;/p&gt;&#10;&lt;h3 id="neue-schlüssel-manuell-auf-vps-ablegen"&gt;Neue Schlüssel manuell auf VPS ablegen&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# ssh-copy-id -i ./ed25519-sk_ssh:server_schallbert_yubikey-main.pub server &#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dabei ist &lt;code&gt;server&lt;/code&gt; der SSH-Alias für meinen VPS. Ich verbinde mittels des bekannten, &amp;ldquo;alten&amp;rdquo; Schlüssels lasse den &amp;ldquo;Neuen&amp;rdquo; ablegen. Doch irgend etwas funktioniert nicht wie erwartet:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key&lt;span style="color:#f92672"&gt;(&lt;/span&gt;s&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, to filter out any that are already installed&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;&#9;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; you think this is a mistake, you may want to use -f option&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="schlüssel-erfolgreich-übertragen"&gt;Schlüssel erfolgreich übertragen?&lt;/h3&gt;&#10;&lt;p&gt;Ich logge mich also auf meinem Server ein und prüfe, ob der neue Schlüssel tatsächlich in der &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; Datei auftaucht. Tut er nicht. Also führe ich das obige Kommando erneut aus, diesmal mit aktivierter &lt;code&gt;-f (Force)&lt;/code&gt; Option. Und tatsächlich, die Übertragung klappt!&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# ssh-copy-id -f -i ./ed25519-sk_ssh:server_schallbert_yubikey-main.pub server &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Number of key&lt;span style="color:#f92672"&gt;(&lt;/span&gt;s&lt;span style="color:#f92672"&gt;)&lt;/span&gt; added: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Now try logging into the machine, with: &lt;span style="color:#e6db74"&gt;&amp;#34;ssh -i &amp;lt;identityFile&amp;gt; &amp;#39;server&amp;#39;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;and check to make sure that only the key&lt;span style="color:#f92672"&gt;(&lt;/span&gt;s&lt;span style="color:#f92672"&gt;)&lt;/span&gt; you wanted were added.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Zum Testen wie empfohlen nun mit dem neuen Schlüssel einloggen und&amp;hellip; Läuft! Erst jetzt können die alten Schlüssel sicher gelöscht werden: &lt;code&gt;shred -u &amp;lt;path/to/private/key&amp;gt;&lt;/code&gt;&lt;/p&gt;&#10;&lt;h3 id="auf-direktem-wege-ohne-ssh-copy-id"&gt;Auf direktem Wege ohne ssh-copy-id&lt;/h3&gt;&#10;&lt;p&gt;Will man den neuen Schlüssel ohne zusätzliche Werkzeuge bekannt machen, so kann man den Inhalt der öffentlichen Schlüsseldatei &lt;code&gt;&amp;lt;key-filename&amp;gt;.pub&lt;/code&gt; auch direkt selbst in eine neue Zeile von &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; schreiben. Dafür benötigt man lediglich eine offene SSH-Verbindung zum Ziel.&lt;/p&gt;&#10;&lt;h2 id="überlegungen-zu-komfort-und-bedienbarkeit"&gt;Überlegungen zu Komfort und Bedienbarkeit&lt;/h2&gt;&#10;&lt;p&gt;Wenn ich mir meine SSH-Verbindungen so ansehe, dann teilen sie sich grob in zwei Kategorien:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;Admin-Zugänge&lt;/strong&gt; (VPS, NAS, Raspberry Pi) und Signaturen&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Protokoll-Zugänge&lt;/strong&gt; (Git etc.)&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Erstere erfordern aus meiner Sicht ein höheres Sicherheitsniveau. Zudem nutze ich sie nicht so häufig wie Versionsverwaltungen. Daher empfehle ich für die erste Kategorie &lt;code&gt;verify-required&lt;/code&gt; und würde dies bei der Zweiten weglassen. Mit der Option &lt;code&gt;no-touch-required&lt;/code&gt; würde ich ebenso verfahren: falls alle paar Minuten ein Commit hochgeladen werden soll, werden ständige Berührungen des Schlüssels schnell irritierend.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Alle Optionen bekommt man bei Aufruf des Handbuchs per &lt;code&gt;man ssh-keygen&lt;/code&gt; angezeigt.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Hardware Security Keys</title><link>https://blog.schallbert.de/hardware-security-keys-yubikey/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/hardware-security-keys-yubikey/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-04-25-yubikey_thumb.avif"&#10; class="post-cover"&#10; alt="Image: Browser (Firefox) login process for passkey usage: PIN, then touch"&#10; title="Hardware Security Keys" /&gt;&#10;&lt;h2 id="begriffsabgrenzung-tpm-und-hsk"&gt;Begriffsabgrenzung: TPM und HSK&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: center"&gt;Akronym&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: center"&gt;Bezeichnung&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: center"&gt;Kurzbeschreibung&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;TPM&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;Trusted Platform Module&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;Fest verbaut. Schützt das Gerät selbst&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;HSK&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;Hardware Security Key&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: center"&gt;Mobil. Schützt Konten, Zertifikate, private Schlüssel&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Das &lt;a href="https://de.wikipedia.org/wiki/Trusted_Platform_Module" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Trusted Platform Module&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist ein Sicherheitschip, der kryptografische Schlüssel sicher speichert und Funktionen wie Secure Boot, BitLocker-Verschlüsselung und Geräteidentität bereitstellt. Er ist fest auf der Hauptplatine eines Computers oder Smartphones verlötet.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-04-25-this-is-a-yubikey.avif" alt="Image: A Hardware Security Key with USB-C connector called YubiKey5 NFC." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ein &lt;a href="https://en.wikipedia.org/wiki/Hardware_security_module" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Hardware Security Key &lt;em&gt;HSK&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, Hardware-Sicherheitsschlüssel hingegen ist meist mobil und kann mit verschiedenen Geräten verbunden werden. Es kann Schlüssel nicht nur speichern, sondern meist auch kryptografische Operationen (Schlüsselgenerierung, Signatur, Verschlüsselung) ausführen. HSKs sind hochsicher und manipulationsgeschützt konstruiert. Sie können extern zertifiziert bzw. auditiert sein, sodass auch hochsensible Vorgänge wie Zahlungstransaktionen ausgeführt werden können.&lt;/p&gt;&#10;&lt;h2 id="was-bisher-geschah"&gt;Was bisher geschah&lt;/h2&gt;&#10;&lt;p&gt;Ich hörte einen Podcast vom Bundesamt für Sicherheit in der Informationstechnik (BSI) zum Thema &lt;a href="https://www.bsi.bund.de/SharedDocs/Audio/DE/BSI/Update_verfuegbar_Folge40_2024_02_29.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&amp;ldquo;Passkey statt Passwort&amp;rdquo;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Den Empfehlungen folgend habe ich mir ein mobiles Hardware-Sicherheitsmodul (HSM) gekauft, um in Zukunft &lt;a href="https://en.wikipedia.org/wiki/WebAuthn" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Passkeys&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; für meine Dienste zu verwenden. Schon lange bin ich nicht mehr glücklich mit meiner fragmentierten Geheimnisverwaltung aus Zetteln, Browser-Plugins, offline-Passwortmanagern wie &lt;a href="https://en.wikipedia.org/wiki/KeePass" target="_blank" rel="noopener noreferrer" class="external-link"&gt;KeePass&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, auf Festplatte abgelegten &lt;a href="https://en.wikipedia.org/wiki/Secure_Shell" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;em&gt;SSH&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;-Schlüsseln, Backup-Keys und &lt;a href="https://askleo.com/how-should-i-keep-recovery-codes-secure/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Notfallcodes&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; diverser Anbieter.&lt;/p&gt;&#10;&lt;p&gt;Ich möchte endlich mal Ordnung in die Landschaft bringen, ohne mich an einen kommerziellen Anbieter so fest zu binden, dass ich von ihm abhängig werde.&lt;/p&gt;&#10;&lt;h2 id="zielvorstellung"&gt;Zielvorstellung&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Keine Passwörter oder Login-Daten mehr merken müssen&lt;/li&gt;&#10;&lt;li&gt;Möglichst einfache, dabei sichere Verwaltung von Passwörtern aufbauen&lt;/li&gt;&#10;&lt;li&gt;Ordnung in den Accountwust bringen&lt;/li&gt;&#10;&lt;li&gt;Digitalen Nachlass regeln&lt;/li&gt;&#10;&lt;li&gt;Anderen ermöglichen, es mir gleichzutun&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Welche Erwartung ich jetzt schon einmal nehmen kann: Ganz ohne Passwörter oder Pins geht es auch mit dem HSK nicht. Ein Yubikey ist kein Passwortmanager. Er kann zwar Zertifikate, Passkeys und Secrets für OTPs halten, gibt diese Geheimnisse dann aber nicht mehr preis. Mit seiner Hilfe kann man signieren (Authentizität beweisen), Verschlüsseln und abgeleitete Schlüssel erstellen.&lt;/p&gt;&#10;&lt;p&gt;Vielmehr lässt sich die Passwortverwaltung mit Hilfe eines Hardware-Sicherheitsschlüssels aber zentralisieren und vereinfachen, da gängige Passwortmanager z.B. Passkeys unterstützen und so kein &amp;ldquo;Masterpasswort&amp;rdquo; mehr nötig ist.&lt;/p&gt;&#10;&lt;h3 id="yubikey"&gt;Yubikey&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-04-25-yubico-authenticator.avif" alt="Image: Screenshot of Yubico Authenticator&amp;#39;s menu options, showing `Home`, `Accounts`, `Passkeys`, `Certificates`, and `Slots`" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Als HSK nutze ich zwei &lt;a href="https://www.yubico.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Yubikey&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Schlüsselanhänger. Einen zum immer dabeihaben und einen als Zweitschlüssel, welcher an einem sicheren Ort verwahrt wird. Es gibt noch andere Anbieter mobiler Hardware Security Keys wie &lt;a href="https://www.nitrokey.com/de" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Nitrokey&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Die kannte ich vor Verfassen dieses Artikels noch nicht. Das ist bedauerlich, ist ihr Produkt doch quelloffen und damit idealer Testkandidat für mein Blog 😥.&lt;/p&gt;&#10;&lt;h3 id="yubico-authenticator"&gt;Yubico Authenticator&lt;/h3&gt;&#10;&lt;p&gt;Das GUI-Programm &amp;ldquo;Yubikey Authenticator&amp;rdquo; ist nützlich zum Einrichten, Belegen der Funktionstaste (z.B. Bestätigung oder Passwortausgabe als Tastatursimulation) und falls man &lt;a href="https://www.bsi.bund.de/DE/Themen/Verbraucherinnen-und-Verbraucher/Informationen-und-Empfehlungen/Cyber-Sicherheitsempfehlungen/Accountschutz/Zwei-Faktor-Authentisierung/zwei-faktor-authentisierung_node.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;em&gt;2FA&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;-Einmalkennwörter z.B. als &lt;a href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;em&gt;TOTP&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; erzeugen will. Außerdem unterstützt mein Yubikey &lt;a href="https://en.wikipedia.org/wiki/Public_key_infrastructure" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;em&gt;PKI&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, sodass man dort unter anderem auch &lt;a href="https://letsencrypt.org/certificates/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;em&gt;SSL&lt;/em&gt;-Zertifikate&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und private Schlüssel geschützt ablegen kann.&lt;/p&gt;&#10;&lt;h2 id="konzept-des-hsk"&gt;Konzept des HSK&lt;/h2&gt;&#10;&lt;p&gt;Dabei gehört es zum Konzept, dass private Schlüssel entweder direkt auf dem Hardware-Schlüssel erzeugt werden oder - ohne sie lokal dauerhaft zu speichern - zwar auf einem Endgerät erstellt, aber auf dem Yubikey abgelegt werden. Diese verbleiben auf dem Schlüssel und sind nicht exportierbar. Er unterstützt gängige asymmetrische Verfahren wie &lt;em&gt;RSA&lt;/em&gt; und &lt;em&gt;ECDH&lt;/em&gt;. Für Details siehe z.B. &lt;a href="https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/TechnischeRichtlinien/TR02102/BSI-TR-02102.pdf?__blob=publicationFile&amp;amp;v=15" target="_blank" rel="noopener noreferrer" class="external-link"&gt;diese BSI-Publikation über kryptografische Verfahren&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Außerdem wird der Schlüssel die meiste Zeit offline, also ohne Energieversorgung herumliegen und dadurch kaum angreifbar sein. Eine zusätzliche Pin als Schutzmaßnahme bei Verlust des Schlüssels ist ein weiterer Sicherheitsfaktor.&lt;/p&gt;&#10;&lt;h3 id="zweitschlüssel"&gt;Zweitschlüssel&lt;/h3&gt;&#10;&lt;p&gt;Dennoch sollte man sich notieren, welche Zugänge man mit welchem Verfahren schützt, damit die richtigen Accounts im Falle des Verlustes gesperrt werden können. Ein &amp;ldquo;&lt;a href="https://support.yubico.com/s/article/How-to-register-your-spare-key" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Zweitschlüssel&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&amp;rdquo; sollte vorhanden und belegt sein, was fast genauso zeitaufwändig ist wie den Hauptschlüssel zu konfigurieren.&lt;/p&gt;&#10;&lt;h3 id="nfc"&gt;NFC&lt;/h3&gt;&#10;&lt;p&gt;Von der &lt;a href="https://en.wikipedia.org/wiki/Near-field_communication" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;em&gt;NFC&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;-Unterstützung meines Schlüssels hatte ich mir erhofft, Logins noch schneller und leichter hinter mich bringen zu können. Zwei Dinge hier stören mich jedoch: Meinen Laptop kriege ich partout nicht dazu, den Yubikey per NFC zu erkennen. Und auf dem Handy funktioniert die NFC-Erkennung höchstens mittelmäßig. Halte ich den Schlüssel dran, startet die App und bittet mich um Eingabe der Pin. Tue ich dies, muss ich den Schlüssel wieder entfernen und erneut an den Sensor halten &amp;ldquo;Schlüssel nicht bewegen!&amp;rdquo;. Das funktioniert längst nicht jedes mal und wirkt insgesamt ziemlich störanfällig. Schade.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-04-25-nfc-device-manager.avif" alt="Image: NFC shown in device manager of my Windows laptop: Drivers installed but not detecting my Yubikey" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="was-mir-beim-umgang-mit-yubikey-fehlt"&gt;Was mir beim Umgang mit Yubikey fehlt&lt;/h2&gt;&#10;&lt;p&gt;Insgesamt vermisse ich ein paar Komfort-Funktionen beim alltäglichen Umgang mit dem Yubikey.&#10;Bei Klick auf die gewünschten Schlüssel oder Accounts passiert nämlich nichts. Und konfigurieren kann ich da anscheinend auch nichts.&lt;/p&gt;&#10;&lt;p&gt;Bei der Verwendung von &lt;strong&gt;Passkeys&lt;/strong&gt; würde ich mir eine Verknüpfung wünschen. Ein Klick auf den entsprechenden Schlüssel im Authenticator sollte die entsprechende App bzw. das Browserfenster mit der Zielwebsite aufrufen. Da ich das HSK zu diesem Zeitpunkt bereits mit der PIN entsperrt habe, kann sogar der Anmeldevorgang nach Berührung des Schlüssels vollautomatisch stattfinden.&lt;/p&gt;&#10;&lt;p&gt;Ähnliches wünsche ich mir beim Umgang mit &lt;strong&gt;Accounts&lt;/strong&gt;. Warum heißt das Modul &amp;ldquo;Accounts&amp;rdquo;, wenn ich meine Konten nicht direkt darüber aufrufen kann? Auch hier muss ich die entsprechenden WebApps etc. selbst öffnen, den Passwortmanager am Start haben und dann noch die 2FA-Codes vom Authenticator herauskopieren. Das geht bestimmt einfacher.&lt;/p&gt;&#10;&lt;h3 id="touch-password-pin-puk-management-key-passphrase"&gt;Touch, password, PIN, PUK, management key, passphrase&lt;/h3&gt;&#10;&lt;p&gt;Je nach Modul gibt es auch bei der Verwendung des Yubikey zumeist eine Mehr-Faktor-Authentisierung. Das unkomplizierteste Verfahren ist eine Anwesenheitserkennung über eine Berührung des Sticks. Damit kann sichergestellt werden, dass eine Person das HSK aktiv bedient und nicht etwa automatisch eingeloggt werden kann. &lt;em&gt;Touch&lt;/em&gt; kann modulübergreifend angewendet werden; zur Detailkonfiguration komme ich &lt;a href="https://blog.schallbert.de/hardware-security-key-add-ssh/"&gt;in meinem Artikel über SSH&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Das &lt;em&gt;Accounts&lt;/em&gt;-Modul kann optional mittels Passwort, das &lt;em&gt;Passkeys&lt;/em&gt;-Modul per &lt;em&gt;PIN&lt;/em&gt; gesichert werden. Manche Anbieter verlangen die Einrichtung einer PIN als zweiten Faktor sogar.&lt;/p&gt;&#10;&lt;p&gt;Das &lt;em&gt;Certificates&lt;/em&gt;-Modul muss mit &lt;a href="https://docs.yubico.com/yesdk/users-manual/application-piv/pin-puk-mgmt-key.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;PIN, und PUK und Management Key&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; vor unbefugtem Zugriff gesperrt werden. Private Schlüssel oder Zertifikate können optional zusätzlich mit einer &lt;a href="https://www.ssh.com/academy/ssh/passphrase" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Passphrase&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ausgestattet werden, was die Schlüssel selbst schützt (und aus meiner Sicht für den Yubikey keinen Sinn macht, da die privaten Schlüssel ohnehin nicht auslesbar sind).&lt;/p&gt;&#10;&lt;h2 id="funktionsumfang-und-kryptographische-landschaft"&gt;Funktionsumfang und kryptographische Landschaft&lt;/h2&gt;&#10;&lt;p&gt;Da das HSK einen so großen Sicherheitsgewinn verspricht, möchte ich natürlich möglichst viele seiner Fähigkeiten nutzen. Ich habe jetzt ein paar Wochen mit den verschiedenen Modulen herumgespielt und muss feststellen, dass man sich ganz schön hineindenken und viel lernen muss, um alle gut nutzen zu können. Immerhin versteht man danach ungefähr was die Module tun und bekommt einen guten Überblick zu den Themen Internet-Sicherheit, Verschlüsselungsverfahren und -Protokolle.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Accounts&amp;rdquo; und &amp;ldquo;Passkeys&amp;rdquo; sind für Eingabemasken (Plattformen, Webseiten, Shops) und Apps (Mailer, Passwortmanager, Messenger) einfach einzurichten: Absolut empfehlenswert! - schallbert&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Yubikey-Modul&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Beschreibung&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Accounts&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Verwaltung von Konten/Anmeldungen, unterstützt TOTP&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Passkeys&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Passwortloses Anmeldeverfahren durch &lt;a href="https://bitmi.de/it-sicherheitsblog-was-ist-eigentlich-fido2/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;FIDO/WebAuthn&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; (Public-Key-Credential)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Certificates&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Speicherung und Nutzung von Zertifikaten; unterstützt &lt;a href="https://www.openpgp.org/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;OpenPGP&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; (PGP) und &lt;a href="https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/TechnischeRichtlinien/TR02103/BSI-TR-02103.pdf?__blob=publicationFile&amp;amp;v=2" target="_blank" rel="noopener noreferrer" class="external-link"&gt;X.509&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; (PIV)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Slots&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Zwei programmierbare Slots zur Ausgabe statischer Passworte oder Einmalkennwörter&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Kompliziert und aufwändig wird es meiner Meinung nach mit Public/Private Keys und Zertifikaten. Erzeugungs- und Validierungsverfahren hängen vom Betriebssystem oder darauf verfügbaren Programmen ab und Anleitungen greifen teils zu kurz oder widersprechen sich: Nur für Nerds oder Profis. - schallbert&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Gehen wir die Module mal durch.&lt;/p&gt;&#10;&lt;h3 id="2-faktor-authentisierung-yubikey-accounts"&gt;2-Faktor-Authentisierung &amp;ldquo;Yubikey: Accounts&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Der Yubikey kann als Generator für zeitbasierte Einmalkennwörter dienen. Dafür legt man wie in jeder anderen Authenticator-App auch pro Dienst ein Konto an und übergibt einen Sicherheitsschlüssel (Geheimnis, Secret) an den Yubikey. Auf Basis dieses Geheimnisses können bei angestecktem HSK nun Einmalcodes erzeugt werden.&lt;/p&gt;&#10;&lt;p&gt;Da es keine Möglichkeit gibt, diese &amp;ldquo;Secrets&amp;rdquo; vom Yubikey zu exportieren, muss der Vorgang für Zweitschlüssel wiederholt werden. Doch nicht jeder Anbieter sieht die Nutzung mehrerer Authentikatoren vor. Also kann es sinnvoll sein, das Geheimnis in der Zwischenablage&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; zu halten und ihn bei der Konfiguration des/der Zweitschlüssel wiederzuverwenden.&lt;/p&gt;&#10;&lt;h3 id="passkeys-yubikey-passkeys"&gt;Passkeys &amp;ldquo;Yubikey: Passkeys&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Das Passkey-Modul meines Yubikey funktioniert recht gut. Viele Services&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; unterstützen die passwortlose Anmeldung leider noch nicht oder akzeptieren Passkeys nur als zweiten Faktor. Bei anderen Services&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; kann ich das Passwort nicht deaktivieren, sodass ich kaum einen Sicherheitsgewinn sehe oder mir werden Backup-Codes ausgegeben, die im Notfall als zweiter Faktor dienen sollen&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;. Immerhin, ich kann durch den Passkey jetzt bei einigen Diensten die Passwort-TOTP-Anmeldung umgehen und bin schneller eingeloggt.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Die wohl größten Vorteile der Technologie für Nutzerinnen und Nutzer liegen darin, dass Sie in Zukunft zum einen keine Passwörter mehr erstellen und verwalten müssen. Zum anderen sind Passkeys immun gegen die breite Masse bekannter Phishing-Angriffe.&amp;rdquo; - &lt;a href="https://www.bsi.bund.de/DE/Themen/Verbraucherinnen-und-Verbraucher/Informationen-und-Empfehlungen/Cyber-Sicherheitsempfehlungen/Accountschutz/Passkeys/passkeys-anmelden-ohne-passwort_node.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;BSI-Website, Oktober 2025&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Die Authenticator-App braucht man nicht zur Konfiguration von Passkeys. Dies läuft komplett im Browser oder der Kommandokonsole ab. Beide müssen das Handling von Passkeys unterstützen.&lt;/p&gt;&#10;&lt;h3 id="keys--certificates-yubikey-certificates"&gt;Keys &amp;amp; Certificates &amp;ldquo;Yubikey: Certificates&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Um hier etwas Licht hineinzubringen, müssen wir uns erst einmal den Unterschied zwischen Schlüsselpaar und Zertifikat im Kontext der Authentisierung vor Augen führen. In beiden Fallen geht es darum, die Identität des Client zu verifizieren. Vor einem Server (z.B. per SSH), einer Institution (z.B. einer Certificate Authority) oder einem anderen Client (z.B. per &lt;em&gt;PGP&lt;/em&gt;). Somit wird für das Ziel feststellbar, dass es sich tatsächlich um den zur Verbindung berechtigten Client handelt.&lt;/p&gt;&#10;&lt;h3 id="publicprivate-key-pair"&gt;Public/Private Key Pair&lt;/h3&gt;&#10;&lt;p&gt;Das reine Schlüsselpaar (also kein Zertifikat) wird üblicherweise dann verwendet, wenn das Ziel der Kommunikation im Besitz desjenigen ist, der auch über den Client verfügt. Dann nämlich kann der Client zum Aufsetzen der Verbindung den öffentlichen Schlüssel auf dem Host platzieren. Dieses Verfahren wird z.B. bei Einrichten von SSH-Verbindungen und für einige APIs wie bei der Search Console verschiedener Suchmaschinenanbieter angewendet.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-04-25-gitea-ssh-keys.avif" alt="Image: Gitea account security settings: SSH public key stored in Gitea Webapp" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Der Host kann dem Client mit dem öffentlichen Schlüssel verschlüsselte Testnachricht schicken, welche nur mit dem zugehörigen privaten Schlüssel wieder in Klartext umgewandelt werden kann. Schickt der Client den korrekten Klartext zum Host zurück, ist für den Host bewiesen, dass der Verbindungspartner authentisch ist.&lt;/p&gt;&#10;&lt;h3 id="certificates--piv"&gt;Certificates / PIV&lt;/h3&gt;&#10;&lt;p&gt;Doch was, wenn der Host nicht unter der Kontrolle des Clients steht? Wie soll dann sichergestellt werden, dass sich weder Client noch Server für jemand anders ausgibt und einen gefälschten öffentlichen Schlüssel hinterlegt? Genau da kommen Zertifikate zum Einsatz.&lt;/p&gt;&#10;&lt;p&gt;Vereinfacht gesagt enthält das Zertifikat nicht nur den öffentlichen Schlüsselteil, sondern zusätzliche Informationen über den Netzteilnehmer und die gewünschte Verbindung. Nun kommt eine vertrauenswürdige, an der Kommunikation zwischen Client und Server unbeteiligte dritte Partei ins Spiel. In der Private Key Infrastructure &lt;em&gt;PKI&lt;/em&gt; nennt man sie Certificate Authority &lt;em&gt;CA&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Der Netzteilnehmer sendet nun den öffentlichen Schlüssel samt Metadaten über sich selbst an die CA und stellt einen sogenannten &lt;em&gt;CSR&lt;/em&gt;, einen Certificate Signing Request. Die CA prüft nun, ob der Kanal des Teilnehmers auch wirklich diesem gehört.&lt;/p&gt;&#10;&lt;p&gt;Bei einer E-Mail (Stichwort &lt;a href="https://www.gpg4win.org/doc/en/gpg4win-compendium_8.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;PGP/GPG&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) kann die CA beispielsweise einen aus dem Public Key errechneten Hash-Link an die angegebene Mailadresse versenden. Wird der Link geklickt, beweist dies der CA gegenüber, dass die Mailadresse tatsächlich dem Client gehört und sie kann das Zertifikat ausstellen, den CSR also mit ihrem privaten Schlüssel signieren.&lt;/p&gt;&#10;&lt;p&gt;Bei Domains (Stichwort SSL/TLS) kann die CA beim Domain Name Service &lt;em&gt;DNS&lt;/em&gt;-Anbieter prüfen, ob die Angaben zur Anfrage des Servers passen.&lt;/p&gt;&#10;&lt;p&gt;Clients haben die Signaturen gängiger Certificate Authorities eingespeichert (z.B. im Browser). Wird das Zertifikat auf dem Rückweg zwischen Server und Client manipuliert, verändert sich der Hashwert und der Client spuckt eine Zertifikatswarnung aus: &amp;ldquo;invalid certificate&amp;rdquo;.&#10;Jetzt muss man jedoch den Certificate Authorities vertrauen&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt; 🙃&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Der Rechner sollte dafür offline sein. Wenn man supersicher sein will, macht man das Anlernen auf einem Live-Betriebssystem. Das bootet üblicherweise von einem USB-Stick und hat nach dem Herunterfahren alles vergessen.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:2"&gt;&#10;&lt;p&gt;Z.B. Gar nicht: Mailbox, 2FA: Discord und Kraken, zum Zeitpunkt Apr-26. Vielleicht habe ich aber auch nicht die richtigen Einstellungen gefunden?&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:3"&gt;&#10;&lt;p&gt;Z.B. Google, Github und Hetzner, zum Zeitpunkt Apr-26.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:4"&gt;&#10;&lt;p&gt;Auch diese Backup-Codes müssen sicher abgelegt werden. Idealerweise wieder offline, unbedingt aber unabhängig vom HSK.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:5"&gt;&#10;&lt;p&gt;Es gibt Alternativen zu Institutionen wie CAs, siehe &lt;a href="https://de.wikipedia.org/wiki/Web_of_Trust" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Web of Trust&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. So kann die Verifikation der Identität beispielsweise auch peer-to-peer von unbeteiligten Dritten stattfinden.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Giteas act_runner rootless: Startprobleme</title><link>https://blog.schallbert.de/act-runner-dind-failed-to-start-the-child/</link><pubDate>Sat, 28 Mar 2026 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/act-runner-dind-failed-to-start-the-child/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-03-28-act_runner-dind-failed-to-start-child-solved-thumb.avif"&#10; class="post-cover"&#10; alt="Image: Top page crop of OWASP&amp;#39;&amp;#39;s Docker Security Cheat Sheet [Source, downloaded May-26](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html) [License](https://creativecommons.org/licenses/by-sa/4.0/)"&#10; title="Giteas act_runner rootless: Startprobleme" /&gt;&#10;&lt;h2 id="das-fehlerbild"&gt;Das Fehlerbild&lt;/h2&gt;&#10;&lt;p&gt;Mein &lt;a href="https://blog.schallbert.de/gitea-act-runner-dind/"&gt;docker-in-docker (DinD) act_runner&lt;/a&gt; stürzt kurz nach dem Start mit folgender Fehlermeldung ab:&lt;/p&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-03-28-act_runner-dind-failed-to-start-child-problem.avif" alt="Image: console window with docker logs text output: `\[rootlesskit:parent\] error: failed to start the child: fork/exec /proc/self/exe: operation not permitted`" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="issue-ticket-bei-gitea"&gt;Issue-Ticket bei Gitea&lt;/h2&gt;&#10;&lt;p&gt;In der &lt;a href="https://gitea.com/gitea/act_runner/issues/721" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Gitea-Community (issue #721)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; habe ich ein Ticket für das Problem angelegt. Im Folgenden gehe ich auf eigene Erkenntnisse ein und fasse die Diskussion etwas zusammen.&lt;/p&gt;&#10;&lt;h2 id="problem-mit-kernel-berechtigungen"&gt;Problem mit Kernel-Berechtigungen?&lt;/h2&gt;&#10;&lt;p&gt;Der &lt;code&gt;act_runner&lt;/code&gt; muss bei der Docker-in-Docker Konfiguration selbst einen eigenen &lt;a href="https://docs.docker.com/engine/security/#docker-daemon-attack-surface" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Docker Daemon&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; betreiben. Nur so kann der Runner für die &lt;em&gt;Actions&lt;/em&gt; eigene Container erstellen. Dies ist aus Sicherheitsgründen erst einmal nicht erlaubt.&lt;/p&gt;&#10;&lt;h3 id="warum-container-keine-container-starten-dürfen"&gt;Warum Container keine Container starten dürfen&lt;/h3&gt;&#10;&lt;p&gt;Im Kontext des &lt;em&gt;act_runner&lt;/em&gt; führt eine Action Code aus, der selbst Teil des Repository ist. Wird unbemerkt Schadcode in die Action eingeschleust, z.B. von einem &amp;ldquo;Collaborator&amp;rdquo;, so kann dieser bei einer Docker-Installation ohne DinD über den &lt;a href="https://docs.docker.com/engine/security/rootless/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;unter Umständen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; mit &lt;strong&gt;root-Rechten&lt;/strong&gt; ausgestatteten Docker-Daemon &lt;code&gt;dockerd&lt;/code&gt; direkt auf das Hostsystem durchgreifen.&lt;/p&gt;&#10;&lt;h3 id="auswirkungen-falls-trotz-dind-etwas-schief-geht"&gt;Auswirkungen, falls trotz DinD etwas schief geht&lt;/h3&gt;&#10;&lt;p&gt;Bei Anwendung des DinD-Konzeptes kann dies bei einem Angriff lediglich zum Zugriff auf den &lt;code&gt;dockerd&lt;/code&gt; innerhalb des &lt;em&gt;act_runner&lt;/em&gt; Containers führen - und das nur, wenn die Action nicht sauber gekapselt ist. Immer noch nicht gut, aber vertretbar: Bei Neustart des Containers ist der Status quo ante wiederhergestellt. Durchgriff auf das Hostsystem besteht allenfalls indirekt dadurch, dass der Container selbst mit sämtlichen Kernel-Features ausgestattet ist.&lt;/p&gt;&#10;&lt;h3 id="einfache-lösung-dind-container-privileged-starten"&gt;Einfache Lösung: DinD-Container &lt;code&gt;privileged&lt;/code&gt; starten&lt;/h3&gt;&#10;&lt;p&gt;Um dem DinD-Container jetzt die Möglichkeit zu geben eigene &lt;em&gt;Actions&lt;/em&gt; aufzusetzen, kann in der Konfiguration &lt;code&gt;privileged: true&lt;/code&gt; gesetzt werden. Damit werden dem Container &lt;strong&gt;alle Kernel-Fähigkeiten&lt;/strong&gt; eingeräumt. Dies ist laut (dem hervorragend gemachten) &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-3-limit-capabilities-grant-only-specific-capabilities-needed-by-a-container" target="_blank" rel="noopener noreferrer" class="external-link"&gt;OWASP Security Cheat Sheet&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; möglichst zu vermeiden. Sollte &lt;em&gt;act_runner&lt;/em&gt; nun selbst angegriffen werden oder kritische Sicherheitslücken offenbaren, hätte der Eindringling auf diese Weise bereits alle Möglichkeiten, der Kapselung vor dem Host-System zu entgehen.&lt;/p&gt;&#10;&lt;p&gt;Will man den einfachen Weg gehen, sieht die entsprechende &lt;code&gt;docker-compose.yml&lt;/code&gt; wie folgt aus.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# section ACT_RUNNER&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;runner&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gitea/act_runner:latest-dind-rootless&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;container_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gitea-runner&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;privileged&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;And remember: Do not run containers with the &amp;ndash;privileged flag!!!&amp;rdquo; - OWASP&amp;rsquo;s Docker Security Cheat Sheet, 2026 &lt;a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;License&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h3 id="sackgasse-nur-unbedingt-benötigte-rechte-vergeben"&gt;Sackgasse: Nur unbedingt benötigte Rechte vergeben&lt;/h3&gt;&#10;&lt;p&gt;Nach kurzer Recherche finde ich einen &lt;a href="https://www.codestudy.net/blog/can-i-run-docker-in-docker-without-using-the-privileged-flag/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Artikel auf CodeStudy.net&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;, welcher sich mit dem Thema DinD beschäftigt. Hieraus bastele ich Änderungen in meiner &lt;code&gt;docker-compose.yml&lt;/code&gt; zusammen.&lt;/p&gt;&#10;&lt;p&gt;Meine Vorgehensweise:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Änderungen in &lt;code&gt;docker-compose.yml&lt;/code&gt; vornehmen&lt;/li&gt;&#10;&lt;li&gt;Service neu starten &lt;code&gt;docker compose restart &amp;lt;service&amp;gt;&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Logs anschauen &lt;code&gt;docker logs --tail 100 &amp;lt;container-name&amp;gt;&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Falls gestartet: Programm laufen lassen &lt;code&gt;web-app-&amp;gt;repo-&amp;gt;jobs-&amp;gt;rerun_all-jobs&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Bei Fehlschlag: Wiederholen&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Ich beginne mit wenigen, aber sehr mächtigen Rechten. Nach ein paar Iterationen erhalte ich:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# section act_runner DinD-rootless&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;runner&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gitea/act_runner:latest-dind-rootless&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;container_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gitea-runner&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;privileged&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cap_add&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;SYS_ADMIN&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;security_opt&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#66d9ef"&gt;no&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;new-privileges:true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;apparmor:unconfined &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;systempaths=unconfined&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;writable-cgroups=true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aber noch immer fehlen mir Rechte, die außerhalb von Kernel-Privilegien und Sicherheitszonen liegen: Der Docker-Daemon möchte auf &lt;code&gt;sysfs&lt;/code&gt; und &lt;code&gt;proc&lt;/code&gt;, also Systemdateiverzeichnisse und -Prozesse zugreifen können.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;level&lt;span style="color:#f92672"&gt;=&lt;/span&gt;warning msg&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;[rootlesskit:child ] failed to mount sysfs, falling back to read-only mount: operation not permitted&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Das kann ich selbst mit &lt;code&gt;cap_add: ALL&lt;/code&gt; nicht ausgleichen. Zudem wird die Quellenlage im Internet auf dieser technischen Ebene echt dünn (und ich bin kein Docker-Spezialist). Ein &lt;a href="https://zhsj.me/blog/view/dind-without-privileged" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Re-Mount&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; der Dateisysteme könnte hier noch helfen; das erscheint mir aber zu experimentell und fehleranfällig.&lt;/p&gt;&#10;&lt;p&gt;Nach weiteren Stunden Recherche und Trial-and-Error auf meinem Server muss ich &lt;a href="https://github.com/docker-library/docker/issues/546" target="_blank" rel="noopener noreferrer" class="external-link"&gt;@tianon in einer Diskussion auf Github&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zustimmen: Der Docker Daemon benötigt so viele Rechte und Fähigkeiten, dass man gleich bei &lt;code&gt;privileged: true&lt;/code&gt; bleiben kann und sich nicht mit einer Armada an &lt;code&gt;CAP_ADD&lt;/code&gt; und System-bind-mounts herumschlagen muss.&lt;/p&gt;&#10;&lt;h3 id="zukünftige-lösung-virtualisierung-oder-daemonless"&gt;Zukünftige Lösung: Virtualisierung oder Daemonless&lt;/h3&gt;&#10;&lt;p&gt;Doch es gibt möglicherweise bessere Lösungen: Eigene Container-Laufzeitumgebungen wie &lt;a href="https://github.com/nestybox/sysbox" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Sysbox&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Die Box hat selbst auf dem Hostsystem keine besonderen Rechte, ähnlich wie in einer virtuellen Maschine scheint sie in ihr laufenden Applikationen aber alle Rechte und Fähigkeiten zur Verfügung stellen zu können.&lt;/p&gt;&#10;&lt;p&gt;Installation und Inbetriebnahme bis hin zu funktionierenden Job-Containern führt für diesen Post zu weit. Daher muss ich vorerst auf andere &lt;a href="https://www.jaburjak.cz/posts/docker-in-docker-unprivileged/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Quellen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; verweisen.&lt;/p&gt;&#10;&lt;p&gt;Auch das Umschwenken &lt;a href="https://tiendu.github.io/2025/04/18/dind.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;von Docker auf Podman&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist eine mögliche Lösung: Podman ist Daemonless, rootless und bietet ähnlichen Funktionsumfang wie Docker. Ein Umzug nach Podman wäre für mich ein eigenes Projekt und passt nicht in den Kontext dieses Posts.&lt;/p&gt;&#10;&lt;h2 id="linux-security-modules-seccomp-apparmor-selinux"&gt;Linux Security Modules (seccomp, AppArmor, SELinux)&lt;/h2&gt;&#10;&lt;p&gt;Begriffsklärung: &lt;a href="https://www.kernel.org/doc/html/latest/admin-guide/LSM/index.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;LSM&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ermöglichen verschiedene Sicherheitsprüfungen und -Sperren auf Kernelebene. Durch &amp;ldquo;Mandatory Access Control&amp;rdquo; können Sicherheitserweiterungen wie AppArmor Kernel-Fähigkeiten für einzelne Anwendungen steuern und Zugriffe falls nötig sperren.&lt;/p&gt;&#10;&lt;h3 id="apparmor"&gt;Apparmor&lt;/h3&gt;&#10;&lt;p&gt;Ich betreibe meinen Server (VPS) mit Ubuntu. Dieses Betriebssystem hat in den aktuelleren Versionen &amp;ldquo;user namespace creation restrictions&amp;rdquo; in AppArmor integriert, was das Betreiben von &lt;em&gt;appimages&lt;/em&gt;, &lt;em&gt;WebApps&lt;/em&gt; und eben auch &lt;em&gt;Containers&lt;/em&gt; mit höheren Rechten verhindert.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Unprivileged user namespaces are a feature in the Linux kernel [&amp;hellip;]; it enables unprivileged users to gain administrator (root) permissions within a confined environment [&amp;hellip;]&amp;rdquo; - mbelair, Ubuntu Discourse, as of May-2026, &lt;a href="https://discourse.ubuntu.com/t/understanding-apparmor-user-namespace-restriction/58007" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Ubuntu Discourse Website&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Kurzgesagt wurde dieses Werkzeug als &lt;a href="https://de.wikipedia.org/wiki/H%C3%A4rten_%28Computer%29" target="_blank" rel="noopener noreferrer" class="external-link"&gt;security-hardening-Maßnahme&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; entwickelt, um Angriffsfläche auf dem Host-System bei Ausführung von Programmen zu reduzieren, die höhere Rechte erfordern.&lt;/p&gt;&#10;&lt;h3 id="einfache-lösung-komplett-abstellen"&gt;Einfache Lösung: Komplett abstellen&lt;/h3&gt;&#10;&lt;p&gt;Die Dampfhammermethode stellt das Feature für Benutzernamensräume komplett kalt. Wir teilen AppAmor mit, dass Drittprogramme wie in alten Betriebssystemversionen auch ohne Einschränkungen Kernel-Features oder höhere Rechte nutzen dürfen. Den zugehörigen Befehl habe ich aus dem &lt;a href="https://askubuntu.com/questions/1511854/how-to-permanently-disable-ubuntus-new-apparmor-user-namespace-creation-restric" target="_blank" rel="noopener noreferrer" class="external-link"&gt;askubuntu-Forum&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; entliehen.&lt;/p&gt;&#10;&lt;p&gt;Er schaltet die Einschränkung von Nicht-Admin-Benutzernamensräumen (die Übertragung ins Deutsche tut schon fast weh) für diese Sitzung ab, indem er die Kernelparameter zur Laufzeit umschreibt (&lt;code&gt;-w&lt;/code&gt;).&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbertTestsThis@machine:~# sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ich halte den Befehl zur Fehlersuche für sehr hilfreich. Möchte man herausfinden, ob das gewünschte Programm wegen AppArmor nicht startet:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Obiges Kommando eingeben&lt;/li&gt;&#10;&lt;li&gt;das Drittprogramm, den Container etc. testen&lt;/li&gt;&#10;&lt;li&gt;durchbooten. Oder den Befehl mit &lt;code&gt;=1&lt;/code&gt; eingeben. Danach ist der Ursprungszustand wiederhergestellt.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Möchte man diese Verwundbarkeit permanent erhalten, so gibt man ein:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;makeVulnerability@machine:~# echo &lt;span style="color:#e6db74"&gt;&amp;#39;kernel.apparmor_restrict_unprivileged_userns = 0&amp;#39;&lt;/span&gt; | sudo tee /etc/sysctl.d/20-apparmor-donotrestrict.conf&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;makeVulnerability@machine:~# sudo shutdown -r now&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dies alleine stellt noch keine Sicherheitslücke dar. Es ist jetzt nur prinzipiell einfacher geworden, eventuelle Schwächen im Kernel auszunutzen und der &amp;ldquo;Sandbox&amp;rdquo; des Containers zu entrinnen.&lt;/p&gt;&#10;&lt;h3 id="richtige-lösung-für-jede-anwendung-justieren"&gt;Richtige Lösung: Für jede Anwendung justieren&lt;/h3&gt;&#10;&lt;p&gt;Es gibt Anwendungen (wie meine DinD-Version von &lt;code&gt;act_runner&lt;/code&gt;), die benötigen zwingend erweiterte Rechte um ordnungsgemäß zu funktionieren. Und nur diese sollten die Möglichkeit erhalten, Nicht-Admin-Benutzernamensräume zu erhalten.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://docs.docker.com/engine/security/apparmor/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Docker selbst&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; hat &lt;em&gt;AppAmor&lt;/em&gt; einen Abschnitt in der Dokumentation gewidmet. Um zur Lösung für den DinD-Runner zu gelangen, benötigt es eine &lt;a href="https://www.spad.uk/posts/rootless-dind-noble/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Transferleistung (danke, @thespad)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Zur Wiederholung nochmal die Fehlermeldung von oben:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;rootlesskit:parent&lt;span style="color:#f92672"&gt;]&lt;/span&gt; error: failed to start the child: fork/exec /proc/self/exe: operation not permitted&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner | s6-svwait: fatal: some services reported permanent failure or their supervisor died&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Das Logfile sagt aus, dass der &lt;code&gt;parent&lt;/code&gt; (Docker-Daemon im Container) sein &lt;code&gt;child&lt;/code&gt; (Runner-Container) nicht starten kann, weil er für andere Teilnehmer (&lt;code&gt;fork&lt;/code&gt;) selbst keine Prozesse (&lt;code&gt;/proc/self&lt;/code&gt;) erstellen kann. Wer ist der User dieses Daemons? &lt;code&gt;rootlesskit&lt;/code&gt;.&#10;Genau hier hakt unsere Lösung ein: Wir müssen in der &lt;code&gt;docker-compose.yml&lt;/code&gt; das AppArmor-Profil für &lt;em&gt;rootlesskit&lt;/em&gt; im DinD-Container des act_runner freigeben.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# section act_runner DinD-rootless&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;runner&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gitea/act_runner:latest-dind-rootless&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;container_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gitea-runner&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;privileged&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;security_opt&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;apparmor=rootlesskit&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Entsprechend gut sieht jetzt auch das Logfile aus. Fertig!&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-03-28-act_runner-dind-failed-to-start-child-solved.avif" alt="Image: act_runner rootless-DinD logfile with --privileged:true and --security_opt:apparmor=rootlesskit, showing a clean startup" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Ich habe das Gefühl, dass jener Post mit &amp;ldquo;KI&amp;rdquo; geschrieben wurde. Wenn es konkret wird und es um &amp;ldquo;critical volume mounts&amp;rdquo; geht, stehen dort nämlich keine echten System-Pfade mehr und der Artikel wird insgesamt so vage, dass ich den Ratschlägen folgend gar nicht bis zum Ziel komme.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Edelstahl V2a bzw. AISI304 gravieren</title><link>https://blog.schallbert.de/engrave-aisi304-stainless-steel/</link><pubDate>Sun, 08 Mar 2026 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/engrave-aisi304-stainless-steel/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-03-08-Engrave-Stainless-thumb.avif"&#10; class="post-cover"&#10; alt="Image: A tapered engraving tool for stainless stell application"&#10; title="Edelstahl V2a bzw. AISI304 gravieren" /&gt;&#10;&lt;h2 id="warum-dieser-versuch"&gt;Warum dieser Versuch?&lt;/h2&gt;&#10;&lt;p&gt;Ich wurde gefragt, ob ich einen Hilfsanker für eine Segelyacht gravieren könne. Dieser &lt;a href="https://ankerplex.eu/wp-content/uploads/2025/06/DATA-SHEET-ANKERPLEX.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Anker&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist aus &lt;code&gt;10mm&lt;/code&gt; starkem Edelstahlblech gefertigt und passt mit Maßen von &lt;code&gt;389x630mm&lt;/code&gt; auf meine Maschine.&lt;/p&gt;&#10;&lt;p&gt;Ich habe noch nie mit dem Material gearbeitet und bin so neugierig, dass ich den Auftrag annehme.&lt;/p&gt;&#10;&lt;h2 id="das-material"&gt;Das Material&lt;/h2&gt;&#10;&lt;p&gt;Der Anker besteht aus Edelstahl der Sorte &lt;a href="https://en.wikipedia.org/wiki/SAE_304_stainless_steel" target="_blank" rel="noopener noreferrer" class="external-link"&gt;AISI304&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Andere Namen für diese Legierung sind &lt;code&gt;18/10&lt;/code&gt;, &lt;code&gt;18/8&lt;/code&gt; bezogen auf ihren Chrom/Nickel-Gehalt oder &lt;code&gt;V2A/A2&lt;/code&gt; nach ihrer Entwicklung aus der Versuchsschmelze bzw. ihre Herstellung &amp;ldquo;&lt;strong&gt;A&lt;/strong&gt;ir hardened&amp;rdquo; und der Legierungsgruppe. Sie ist verhältnismäßig gut formbar und wird daher häufig in industriellen Anwendungen genutzt.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-03-08-Anker-AISI304-total.avif" alt="Image: Arrow-shaped anchor with non-polished steel surface made of AISI304 stainless steel" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Dieser Edelstahl hat für die spanende Bearbeitung jedoch ein paar Nachteile: Seine Wärmeleitfähigkeit ist recht gering. Daher geht wenig Wärme ins Material, was den Fräser umso heißer werden lässt. Dazu ist das Material zäh. Der Span löst sich nicht gut vom Werkzeug, es entsteht leicht eine &lt;a href="https://de.wikipedia.org/wiki/Aufbauschneide" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Aufbauschneide&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Das erzeugt Unsauberkeiten in der Fräsbahn. Schiebt der Fräser Späne vor sich her, wird das Material nicht vollständig geschnitten und es neigt zur Gratbildung. Außerdem ist der Edelstahl kaltverfestigend: Reibt oder Steht der Fräser am Material, verändert der von ihm ausgehende Druck die Materialoberfläche. Sie wird härter als das Werkzeug selbst.&lt;/p&gt;&#10;&lt;p&gt;All diese Eigenschaften zusammengenommen sorgen für hohen Werkzeugverschleiß und großen Kühlbedarf. Die Schnittparameter müssen außerdem in einem engen Korridor gehalten werden.&lt;/p&gt;&#10;&lt;h2 id="das-werkzeug"&gt;Das Werkzeug&lt;/h2&gt;&#10;&lt;p&gt;Ich wähle einen Gravierfräser von &lt;a href="https://shop.vhf.de/articleGroups/Gravierfraeser-fuer-Edelstahl-W_G_GFV.htm" target="_blank" rel="noopener noreferrer" class="external-link"&gt;vhf&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; speziell für die Bearbeitung von Edelstahl. Mit &lt;code&gt;6mm&lt;/code&gt; Schaftdurchmesser, jedoch nur &lt;code&gt;4mm&lt;/code&gt; Schneidendurchmesser, einer kurzen geraden Schneide, &lt;code&gt;60°&lt;/code&gt; Gravurwinkel und &lt;code&gt;0.4mm&lt;/code&gt; Spitzendurchmesser wirkt das Werkzeug gedrungen und stabil. Der Fräser ist mit einer &lt;a href="https://www.harveytool.com/resources/tool-coatings" target="_blank" rel="noopener noreferrer" class="external-link"&gt;AlTiN&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;-Beschichtung versehen.&lt;/p&gt;&#10;&lt;p&gt;Ob die dünne Spitze den Fräskräften und der Wärmeentwicklung standhält?&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-03-08-Gravierfr%c3%a4ser.avif" alt="Image: Coated carbide tapered engrave mill cutter for stainless steel" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="diskussion-um-fräsparameter"&gt;Diskussion um Fräsparameter&lt;/h2&gt;&#10;&lt;p&gt;Im Forum &lt;a href="https://cnczone.nl/viewtopic.php?f=22&amp;amp;t=24339" target="_blank" rel="noopener noreferrer" class="external-link"&gt;cnczone.nl&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; frage ich nach Erfahrungswerten für Tiefenzustellung, Vorschub und Drehzahl für meinen Vollhartmetallfräser.&lt;/p&gt;&#10;&lt;p&gt;Die Internetrecherche&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; liefert mir eine Ziel-Schnittgeschwindigkeit für AISI304 von etwa &lt;code&gt;60m/min&lt;/code&gt; und einen Zahnvorschub zwischen &lt;code&gt;0.02mm und 0.05mm&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Außerdem rufe ich den Fräserhersteller an und frage nach Empfehlungen für optimalen Einsatz auf einer weichen Maschine wie meiner. Der Hersteller sagt, ich müsse unbedingt kühlen. Idealerweise mit reichlich Emulsion auf dem Material. Seine Empfehlung lautet für den ersten Versuch und mit dem Fräser:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;S15000 F600 Z-0.1 (vhf-Beratung)&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Bei Gravuren seien häufige Richtungswechsel zu erwarten, und Ziel solle es sein, die Maschine möglichst gleichmäßig und mit kurzen Beschleunigungswegen zu führen. Das würde Rattermarken weitestgehend vermeiden.&lt;/p&gt;&#10;&lt;p&gt;Aus dem Forum erhalte ich Werte zwischen &lt;code&gt;F240&lt;/code&gt; und &lt;code&gt;F800&lt;/code&gt; bei maximaler Spindeldrehzahl.&lt;/p&gt;&#10;&lt;p&gt;Meine Erfahrungen bei der Verarbeitung von Stahl aus vorherigen Artikeln &lt;a href="https://blog.schallbert.de/milling-secc/"&gt;SECC fräsen&lt;/a&gt; und &lt;a href="https://blog.schallbert.de/milling-steel/"&gt;Stahl bearbeiten&lt;/a&gt; lasse ich einfließen und stelle einen Zahnvorschub von &lt;code&gt;0.04mm&lt;/code&gt; ein. Ich passe die Drehzahl der Schnittgeschwindigkeit von &lt;code&gt;60m/min&lt;/code&gt; an. Ich fahre gern zügig durch das Material, stelle in der Tiefe aber nur sehr vorsichtig zu. Dies ergibt für die vorliegende Schneidengeometrie folgende Werte:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;S30000 F1200 Z-0.1 (Schallbert)&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Damit liege ich mit Drehzahl und Vorschub genau beim Doppelten der Empfehlung von &lt;em&gt;vhf&lt;/em&gt;. Ich wähle den Vorschub so zügig, damit ich weniger Reibungshitze am Fräser erzeuge und mehr Wärme in den Span geht. Die Tiefenzustellung halte ich gering, weil die auf die Konstruktion wirkenden Kräfte so niedriger sind. Davon verspreche ich mir eine höhere Gravurgenauigkeit und bessere Details.&lt;/p&gt;&#10;&lt;p&gt;Die Gesamttiefe der Gravur lege ich auf &lt;code&gt;0.2mm&lt;/code&gt; fest.&lt;/p&gt;&#10;&lt;h3 id="einfluss-der-maschinenkinematik"&gt;Einfluss der Maschinenkinematik&lt;/h3&gt;&#10;&lt;p&gt;Wie oben beschrieben werden Drehzahl und Vorschub aufeinander eingestellt. Dabei ist es wichtig zu prüfen, ob der eingestellte Vorschub überhaupt erreicht werden kann. In Abhängigkeit von der Beschleunigungsfähigkeit der Maschine sowie Größe und Form der Gravur kann es nämlich sein, dass die Maschine durch die vielen Richtungswechsel im Schnitt mit viel geringerem Vorschub unterwegs ist.&lt;/p&gt;&#10;&lt;p&gt;Daher sind Drehzahl und Vorschub an die real erzielbaren Werte anzupassen. Diese können mit der Maschine im Simulationsmodus beobachtet werden. In meinem Falle ist die Maschinenbeschleunigung im Verhältnis zur Größe der Gravur so hoch, dass ich fast immer in Zielgeschwindigkeit operieren kann. Beispielrechnung gefällig?&lt;/p&gt;&#10;&lt;p&gt;Die Maschine benötigt folgende Zeitspanne, um vom Stillstand auf den eingestellten Vorschub von &lt;code&gt;F1200&lt;/code&gt; zu kommen:&lt;/p&gt;&#10;$$a_{machine} = 400\frac{mm}{s^2}, v_{feedrate} = 1200\frac{mm}{min} = 20\frac{mm}{s}$$$$t = \frac{20}{400} = 50ms$$&lt;p&gt;Ich habe also nur sehr kurze Zeiten, in denen die Maschine mehr am Material reibt als schneidet.&lt;/p&gt;&#10;&lt;h2 id="vorbereitung-des-gravurvorganges"&gt;Vorbereitung des Gravurvorganges&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-03-08-AISI304-Cooling.avif" alt="Image: Material on the milling bed. Vacuum on, cooling fluid applied. Ready to mill." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich verarbeite nur ganz selten Metall. Ich habe also keine fertige Schneid- oder Kühlemulsion vorliegen. Als Ersatz nehme ich Ballistol-Spray und etwas Wasser zur Hand. Das Öl reibe ich in die Oberfläche ein. Hinzu füge ich für die Fläche von &lt;code&gt;10x15cm&lt;/code&gt; etwa ein Esslöffel Wasser.&#10;Beides bildet zu meinem Erstaunen von selbst eine Emulsion.&lt;/p&gt;&#10;&lt;p&gt;Ich habe vor, den Anker per Vakuum zu spannen und bringe ihn mittels Passstiften auf dem Frästisch mittig in Position. Die Aussparungen für Griff und Stieleinsatz schließe ich mit beschichtetem Butterbrotpapier (&lt;em&gt;Pro-Tipp! Günstig, super reißfest, umweltschonend, fast luftdicht, gut zu verarbeiten&lt;/em&gt;).&lt;/p&gt;&#10;&lt;p&gt;Nach Einschalten der Vakuumpumpe muss ich leider feststelen, dass der Anker auf einer Seite nicht ganz plan aufliegt. Ich bekomme statt &lt;code&gt;-0.7bar&lt;/code&gt; nur knapp &lt;code&gt;-0.4bar&lt;/code&gt; hin. Das ist für die bei der Gravur wirkenden, geringen Kräfte nicht dramatisch. Die Linienbreite allerdings wird so kaum konstant zu halten sein.&lt;/p&gt;&#10;&lt;p&gt;Meine Lösung ist nun, das Fräsmotiv herunter zu skalieren. Durch die geringere Größe wirken sich Unterschiede in der Linienbreite kaum noch aus.&lt;/p&gt;&#10;&lt;h2 id="durchführung"&gt;Durchführung&lt;/h2&gt;&#10;&lt;p&gt;Bevor ich die Gravur durchführe lasse ich die Maschine im Simulationsmodus laufen. Dabei entdecke ich noch zwei sehr langsame Eintauchvorgänge. Das CAM erstellt sie vermutlich wegen der von mir eingestellten Eintauchrampe, welche auf sehr kurzen Linienzügen erfolgt.&#10;Dies korrigiere ich mit manuell vorgegebenen Eintauchpunkten.&lt;/p&gt;&#10;&lt;p&gt;Nun lasse ich den Fräsjob durchlaufen. Die Emulsion bleibt trotz Spindelgebläse an Ort und Stelle. Die Geräuschentwicklung ist bei der ersten Tiefenzustellung weit geringer als bei der Zweiten - wird jedoch nie unangenehm. Vibrationen treten kaum auf. Der Fräsvorgang dauert auch bei dem aufwändigeren Logo kaum fünf Minuten.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-03-08-Gravur-Petrel.avif" alt="Image: Petrel logo engraved onto stainless steel anchor" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="ergebnis"&gt;Ergebnis&lt;/h2&gt;&#10;&lt;p&gt;Ich bin hochzufrieden. So ein gutes Ergebnis hatte ich auf meiner Hobbymaschine auf Basis meiner Beobachtungen in den Foren nicht erwartet. Praktisch keine Rattermarken, saubere Fräskanäle und nur an den Stopp- und Austauchpunkten leichte Gratbildung. Das Material bleibt jedoch im Fräskanal und erzeugt keine fühlbaren Kanten. Auch im Detail ist das Fräsbild noch ziemlich ansehnlich.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-03-08-Gravur-Ford.avif" alt="Image: Ford logo engraved onto stainless steel anchor" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Das folgende Video zeigt ein paar Details des Fräsvorganges. Meine letzte offene Frage: Ist das leicht raue Geräusch des Fräsers bei der zweiten Tiefenzustellung ein Hinweis auf größeren Werkzeugverschleiß durch Materialverfestigung?&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/7KNuywPo47HWq85FXDmyJE" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Engraving Stainless Steel&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;&lt;a href="https://www.machining-custom.com/blog/304-stainless-steel-cnc-turning-characteristics-and-technology.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Quelle1: VNT Hardware&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; &lt;a href="https://www.machiningdoctor.com/mds/?matId=1750" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Quelle2: Machiningdoctor&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; &lt;a href="https://www.cncoptimization.com/resources/tables/stainless-speeds-feeds/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Quelle3: CNC Optimization&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Werkzeugvermessung automatisieren</title><link>https://blog.schallbert.de/edingcnc-macro-explained/</link><pubDate>Tue, 10 Feb 2026 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/edingcnc-macro-explained/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-02-10-tls-thumb.avif"&#10; class="post-cover"&#10; alt="Image: Drawing of Tool Length Sensor on stock material with distance spindle collet, visualizing Z0 measurement"&#10; title="Werkzeugvermessung automatisieren" /&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-02-10-tls.avif" alt="Image: Image: Drawing of Tool Length Sensor on stock material with distance to the spindle collet, visualizing Z0 measurement" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Mich erreichen immer wieder Fragen zu meinem &lt;a href="https://blog.schallbert.de/macros-for-cnc/"&gt;Post über CNC-Makros&lt;/a&gt;. In diesem Beitrag möchte ich ein paar Antworten geben und Details zur Vermessung und Kompensation verschiedener Werkzeuglängen teilen.&lt;/p&gt;&#10;&lt;h2 id="frage-wie-berechnet-das-makro-die-werkzeuglänge"&gt;Frage: Wie berechnet das Makro die Werkzeuglänge?&lt;/h2&gt;&#10;&lt;p&gt;Im Abschnitt &lt;a href="https://blog.schallbert.de/macros-for-cnc/#function-measure-tool-length"&gt;Werkzeuglänge Vermessen&lt;/a&gt; steht folgender Code:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zSafety&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ; &lt;span style="color:#75715e"&gt;# Go to safety height (machine coordinates)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; X&lt;span style="color:#f92672"&gt;[&lt;/span&gt;xPosTls&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Y&lt;span style="color:#f92672"&gt;[&lt;/span&gt;xPosTls&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ; &lt;span style="color:#75715e"&gt;# Go to tool length sensor&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zSpindleTip &lt;span style="color:#f92672"&gt;+&lt;/span&gt; toolLengthEst &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Move Z down to 10mm above estimated tool tip&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Was passiert in den Zeilen &lt;code&gt;1-3&lt;/code&gt;?&#10;Wenn &lt;code&gt;Z=0&lt;/code&gt; im Maschinenkoordinatensystem (&lt;code&gt;G53&lt;/code&gt;) die höchste Position ist, sind &lt;code&gt;zSpindleTip&lt;/code&gt; und &lt;code&gt;toolLengthEst&lt;/code&gt; dann negative Werte?&lt;/p&gt;&#10;&lt;h3 id="antwort"&gt;Antwort:&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Diese Zeile fährt die Maschine zum in Variable &lt;a href="https://github.com/Schallbert/macro_edingCNC_sorotec/blob/a547e36c0f47490464e1fdb56d1a4b0e706b44fe/macro.cnc#L341" target="_blank" rel="noopener noreferrer" class="external-link"&gt;#4506&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; hinterlegten Wert. Bei mir ist er &lt;code&gt;0&lt;/code&gt;, also maximale Z-Höhe. Dieser Wert bleibt permanent gespeichert (siehe &lt;a href="https://github.com/Schallbert/macro_edingCNC_sorotec/tree/main?tab=readme-ov-file#permanent" target="_blank" rel="noopener noreferrer" class="external-link"&gt;hier&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) und man muss ihn vorher im Makro-Setup-Dialog festlegen.&lt;/li&gt;&#10;&lt;li&gt;Führt eine Bewegung zum Werkzeuglängensensor (WZLS) im Maschinenkoordinatensystem aus. Dafür habe ich meinen Sensor an einem festen Punkt auf der Maschine platziert.&lt;/li&gt;&#10;&lt;li&gt;Mit &lt;code&gt;zSpindleTip&lt;/code&gt; (Variable &lt;code&gt;#4509&lt;/code&gt;) ist die Höhe auf der &lt;code&gt;Z-Achse&lt;/code&gt; gemeint, in der die Spindelnase den Werkzeuglängensensor freitastet. Dieser Wert ist negativ. Weshalb? Der größte Wert der Z-Achse ist &lt;code&gt;Null&lt;/code&gt;, wenn sie sich ganz am oberen Anschlag der Maschine befindet. Senkt man dia Achse von hier aus zum WZLS ab, bewegt man sie in negative Z-Richtung. Addiert man die geschätzte Werkzeuglänge &lt;code&gt;toolLengthEst&lt;/code&gt; sowie eine Sicherheitshöhe von &lt;code&gt;10mm&lt;/code&gt; dazu (falls man sich bei der Werkzeuglängenschätzung vertan hat), bleibt aber unter dem mechanischen Anschlag der Z-Achse, so ist der Wert noch immer negativ. Die Spindelnase hat nun aber genügend Luft zum WZLS, weil jetzt virtuell Werkzeug und Sicherheitshöhe hinzuaddiert wurden.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Pro Tipp: Im &amp;ldquo;Variables&amp;rdquo; Tab von EdingCNC kann man sich Werte von Variablengruppen anzeigen lassen. Man gibt eine Gruppe ein, z.B. &lt;code&gt;#4500&lt;/code&gt; und erhält die Live-Werte aller zehn Variablen.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Um das gut nachvollziehen zu können bietet sich an, die CNC-Software zum Ausprobieren nebenbei im Simulationsmodus laufen zu lassen.&#10;Konkretes Beispiel:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zSafety &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# machine coordinates, Z-axis at top notch&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zSpindleTip &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;86&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;43&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# machine coordinates, where empty spindle collet touches TLS&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;toolLengthEst &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;35&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# the length the tool is protruding from spindle collet&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;86&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;43&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;35&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#75715e"&gt;# = -41.32mm, so below max Z height&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Die Benennung der Variablen habe ich in meinem Makro-Repository &lt;a href="https://github.com/Schallbert/macro_edingCNC_sorotec/tree/main?tab=readme-ov-file#permanent" target="_blank" rel="noopener noreferrer" class="external-link"&gt;auf Github&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; abgelegt.&lt;/p&gt;&#10;&lt;h3 id="frage-wie-berechne-ich-die-werkstückhöhe-nach-einer-werkzeugvermessung"&gt;Frage: Wie berechne ich die Werkstückhöhe nach einer Werkzeugvermessung?&lt;/h3&gt;&#10;&lt;p&gt;Folgendes Szenario: Man vermisst das Werkzeug und bestimmt anschließend per Makro die Werkstückhöhe. Wenn ich nun die Werkzeuglänge verändere ohne das Werkzeugwechselmakro aufzurufen, stimmt der Werkstücknullpunkt ja nicht mehr. Mit einem kürzeren Werkzeug liegt der Nullpunkt &amp;ldquo;zu tief&amp;rdquo;, bei einem Längeren &amp;ldquo;zu hoch&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Wie kann man dieses Problem lösen?&lt;/p&gt;&#10;&lt;h3 id="antwort-mehrere-lösungswege"&gt;Antwort: Mehrere Lösungswege&lt;/h3&gt;&#10;&lt;p&gt;Aus meiner Sicht gibt es hier drei Lösungswege.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Werkzeuglängenänderung immer über das Werkzeugwechselmakro laufen lassen. Man kann dort ja dieselbe Werkzeugnummer erneut eingeben.&lt;/li&gt;&#10;&lt;li&gt;Den Z-Nullpunkt über die Werkstückvermessung neu setzen (nicht empfohlen&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;)&lt;/li&gt;&#10;&lt;li&gt;Ohne Makro: Per Kommando &lt;code&gt;G92&lt;/code&gt; das Z-Koordinatensystem verschieben. Beispiel folgt.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-02-10-Enter-G92.avif" alt="Image: Coordinate shift G92 executed in the CNC software&amp;#39;s control terminal" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="lösung-3-koordinatensystem-verschieben"&gt;Lösung #3: Koordinatensystem verschieben&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-02-10-coordshift-before.avif" alt="Image: CNC&amp;#39;s coodinate window showing machine and workpiece coordinates before shift to reflect a change of tools" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Man kann das Werkstückkoordinatensystem mit den Befehl &lt;a href="https://www.linuxcnc.org/docs/html/gcode/g-code.html#gcode:g92" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;code&gt;G92 Coordinate System Offset&lt;/code&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; verschieben. Führt man ihn aus, so wird die ihm übergebene Zahl der angewählten Achse als neuer Wert im Koordinatensystem übernommen.&lt;/p&gt;&#10;&lt;p&gt;In diesem Fall wollen wir ja nur die relative Position verschieben und damit den Unterschied der Werkzeuglängen zueinander ausgleichen. Machen wir es mal konkret:&lt;/p&gt;&#10;&lt;p&gt;Nehmen wir an, das alte Werkzeug &lt;code&gt;T17&lt;/code&gt; hat eine freie Länge von &lt;code&gt;32mm&lt;/code&gt; und das neue Werkzeug (wieder &lt;code&gt;T17&lt;/code&gt;, denn wir spannen in diesem Beispiel nur um oder setzen ein Schlichtwerkzeug mit gleichen Schnittdaten ein) eine freie Länge von &lt;code&gt;24mm&lt;/code&gt;. Der neue Wert ist kürzer, und daher muss der Nullpunkt um &lt;code&gt;8mm&lt;/code&gt; nach unten wandern. &amp;ldquo;Nach unten&amp;rdquo; ist im Koordinatensystem der Z-Achse ein negativer Wert.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;&lt;span style="color:#75715e"&gt;# Pseudocode&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;&lt;span style="color:#75715e"&gt;# (CamelCaseWord steht für eine Variablennummer wie #5003):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;G92&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;AktuelleZPosition&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;AlteWerkzeuglänge&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NeueWerkzeuglänge&lt;/span&gt;)&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;&lt;span style="color:#75715e"&gt;# 20mm + (32mm - 24mm ) = 20mm + 8mm = 28mm &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="verschiebung-werkstücknullpunkt-vs-werkstückkoordinatensystem"&gt;Verschiebung Werkstücknullpunkt vs. Werkstückkoordinatensystem&lt;/h3&gt;&#10;&lt;p&gt;Diese Rechnung mag erst einmal verwirren, versetzt sie das Koordinatensystem doch nach oben. Aber genau das ist erforderlich, damit der Werkstücknullpunkt relativ zur Werkzeugspitze erhalten bleibt: Das Werkzeug ist &lt;code&gt;8mm&lt;/code&gt; kürzer. Da wir den Werkstücknullpunkt nicht nach unten schieben, muss das Koordinatensystem halt nach oben versetzt werden. So wird die nun längere Strecke zwischen Werkzeugspitze und Werkstück kompensiert.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Coordinate System&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Tool tip position Z [mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Workpiece position Z [mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Tool top position Z [mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Workpiece position Z [mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Before shift&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Before shift&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;After shift&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;After shift&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Workpiece&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;20&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;28&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Machine&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-41.42&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-61,42&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-41.42&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-69,42&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2026-02-10-coordshift-after.avif" alt="Image: CNC&amp;#39;s coodinate window showing machine and workpiece coordinates after shift to reflect a change of tools. The shift only affects workpiece coordinates." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Man sieht hier, dass sich durch das Versetzen des Koordinatensystems die Position der Werkzeugspitze im &lt;em&gt;Werkstückkoordinatensystem&lt;/em&gt; verändert. Die Z-Achse wird dabei nicht bewegt. Das &lt;em&gt;Maschinenkoordinatensystem&lt;/em&gt; bleibt von dieser Änderung unberührt. Da die Z-Achse nun aber einen weiteren Weg zum Werkstück zurücklegen muss, liegt seine Position im Maschinenkoordinatensystem jetzt um &lt;code&gt;-8mm&lt;/code&gt; unter dem vorigen Wert.&lt;/p&gt;&#10;&lt;h2 id="rahmenbedingungen"&gt;Rahmenbedingungen&lt;/h2&gt;&#10;&lt;p&gt;Die Makros funktionieren nur unter bestimmten Voraussetzungen:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Die Werkzeuglänge wurde zuvor gemessen bzw. Werkzeugwechsel werden stets per Makro gesteuert und nicht ohne &amp;ldquo;Wissen&amp;rdquo; der Maschine&lt;/li&gt;&#10;&lt;li&gt;Der Werkstücknullpunkt wird bei bereits bekannter Werkzeuglänge ermittelt.&lt;/li&gt;&#10;&lt;li&gt;Es gab keinen Not-Aus oder einen anderen kritischen Fehler. Dies würde den flüchtigen Speicher für die relevanten Variablen löschen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Nach einem Werkzeugwechsel die Werkstückhöhe in Z erneut einzumessen verschiebt meiner Ansicht nach den falschen Parameter. In Wirklichkeit hat sich doch nicht die Höhe des Werkstückes geändert, sondern die Position der Werkzeugspitze. Daher würde ich stets vorziehen, den sich ändernden Parameter anzupassen.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Stahlblech (SECC) bearbeiten</title><link>https://blog.schallbert.de/milling-secc/</link><pubDate>Mon, 22 Dec 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/milling-secc/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-12-22-HTPC-slotted-thumb.avif"&#10; class="post-cover"&#10; alt="Image: additional slots in HTPC case (SECC) for better cooling"&#10; title="Stahlblech (SECC) bearbeiten" /&gt;&#10;&lt;h2 id="das-werkstück"&gt;Das Werkstück&lt;/h2&gt;&#10;&lt;p&gt;Es handelt sich um ein PC-Gehäuse aus &lt;code&gt;0.8mm&lt;/code&gt; starkem, verzinktem Stahlblech &amp;ldquo;SECC&amp;rdquo;. Es stammt aus den frühen 2000ern und wurde kürzlich mit neuem Innenleben ausgestattet.&lt;/p&gt;&#10;&lt;h3 id="die-aufgabe"&gt;Die Aufgabe&lt;/h3&gt;&#10;&lt;p&gt;Durch die nun viel leistungsfähigere Grafikkarte (&lt;a href="https://de.wikipedia.org/wiki/Thermal_Design_Power" target="_blank" rel="noopener noreferrer" class="external-link"&gt;TDP&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; &lt;code&gt;200W&lt;/code&gt;) und das beengte Design als &lt;a href="https://de.wikipedia.org/wiki/Home_Theater_Personal_Computer" target="_blank" rel="noopener noreferrer" class="external-link"&gt;HTPC&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; war die entstehende Verlustwärme nicht im gewünschten Maße abzutransportieren, sodass im Gehäuse unter Last Temperaturen um &lt;code&gt;50°C&lt;/code&gt; vorherrschten. Das ist mir viel zu hoch.&lt;/p&gt;&#10;&lt;h3 id="die-gegebenheiten"&gt;Die Gegebenheiten&lt;/h3&gt;&#10;&lt;p&gt;Die Grafikkarte folgt gängigen aktuellen Designs: Ein metallener Radiator bedeckt die komplette Oberfläche der Platine. Seine Rippen sind in Querrichtung zum Sockel ausgerichtet und die darauf montierten, flachen Lüfter (&lt;code&gt;3x80mm&lt;/code&gt;) drücken die Luft durch die Leitbleche in Richtung Moainboard und Gehäusedeckel.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-12-22-graka-target-1080.avif" alt="Image: The graphics card for which cooling shall be optimized." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Zwischen Grafikkarte und Gehäusedeckel sind leider nur wenige Millimeter Platz. Nun möchte ich also genau an dieser Stelle zusätzliche Löcher in die Abdeckung einbringen. Das Gehäuse besitzt bereits Belüftungsbohrungen mit &lt;code&gt;3mm&lt;/code&gt; Durchmesser in einem &lt;code&gt;10x19mm 45°&lt;/code&gt;-Raster an den Seiten. Ich versuche nun, dieses Muster auch auf die Bohrungen für die Grafikkartenentlüftung anzuwenden. So komme ich auf eine Gesamtzahl von 133 Stück, welche ich der Bequemlichkeit wegen ungern anzeichnen, körnen und von Hand fertigen möchte.&lt;/p&gt;&#10;&lt;p&gt;Also erstelle ich mir eine Zeichnung im CAD und schließlich ein Bohrzyklus-Programm für meine Fräse.&lt;/p&gt;&#10;&lt;h2 id="werkzeug1-3mm-bohrer"&gt;Werkzeug1: 3mm-Bohrer&lt;/h2&gt;&#10;&lt;p&gt;Ich habe zwar noch ein paar Baumarkt-Stahlbohrer aus üblichem Hochgeschwindigkeits-Schnellarbeitsstahl (HSS) herumliegen, diese befinden sich jedoch in teils schlechtem Zustand. Also nehme ich einen noch unbenutzten Stahlbohrer aus meinem Dremel-Sortiment.&lt;/p&gt;&#10;&lt;h3 id="bohrparameter"&gt;Bohrparameter&lt;/h3&gt;&#10;&lt;p&gt;Er hat eine hellgraue, metallene Färbung und sieht unbeschichteten Werkzeugen aus Vollhartmetall so ähnlich, dass ich (fälschlicherweise) annehme, er sei aus Wolframcarbid. Daher wählte ich folgende Parameter:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;3mm VHM-Bohrer für Stahl, 2 Schneiden: S17000 F470 Z-0.2 Rückzug 0.5mm&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Ich arbeite wie üblich komplett trocken, setze also keine Kühlschmierstoffe ein.&lt;/p&gt;&#10;&lt;h3 id="bohrvorbereitung"&gt;Bohrvorbereitung&lt;/h3&gt;&#10;&lt;p&gt;Zur Vorbereitung des Werkstücks bringe ich auf den Abschnitt des Gehäuses Klebeband an, wo später die Löcher sein sollen. Mit der maximalen Bohrtiefe durchstoße ich das Material nur minimal. Da das Klebeband auf diese Weise intakt bleibt, verliere ich keinen Unterdruck durch offenliegende Löcher im Vakuumtisch.&lt;/p&gt;&#10;&lt;p&gt;Ich lege den Gehäusedeckel mit der Außenseite auf das Maschinenbett und decke freie Löcher meines Vakuumtisches mit einer Gummimatte ab. Anschließend bringe ich das Portal in eine Position nahe der Löcher, damit die Spindel im Pfadverlauf nicht mit der Gehäusewand kollidiert. Außerdem entferne ich das Anfahren der Parkposition &lt;code&gt;G28&lt;/code&gt; aus der Fräsdatei, denn nun liegt der Gehäusedeckel im Weg. Nach Hochziehen der Z-Achse von der letzten Bohrung endet das Programm mit &lt;code&gt;M30&lt;/code&gt; (Stop und zurückspulen).&lt;/p&gt;&#10;&lt;h3 id="das-ergebnis"&gt;Das Ergebnis&lt;/h3&gt;&#10;&lt;p&gt;Um es vorwegzunehmen: Der Bohrer hält nicht durch. Dass etwas nicht in Ordnung ist, merke ich nach wenigen Sekunden am Geräusch der Maschine: Sie ist plötzlich viel leiser als vorher.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-12-22-hss-meltdown.avif" alt="Image: The HSS drill only lives to the fourth hole of 133, then its tip is gone." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Ich schaffe auf diese Weise schlappe 3 von 133 Löchern. Danach befindet sich die Spitze des Bohrers in einem der halb fertiggestellten Löcher und scheint mit diesem verschweißt.&lt;/p&gt;&#10;&lt;h3 id="problemanalyse"&gt;Problemanalyse&lt;/h3&gt;&#10;&lt;p&gt;Anscheinend war der Bohrer gar nicht aus Vollhartmetall, und damit bin ich mit völlig ungeeigneten Parametern unterwegs gewesen. Ich habe die Symptome in zwei Foren gegeben (&lt;a href="https://hobbyline.info/forum/index.php?thread/687-2-schneider-stahlblech-wie-anwenden/&amp;amp;postID=12837#post12837" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Hobbyline&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; sowie &lt;a href="https://www.cnczone.nl/viewtopic.php?t=23051" target="_blank" rel="noopener noreferrer" class="external-link"&gt;cnczone&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) und ich erhalte die Information, dass ich besser mit &lt;code&gt;S1000 F30&lt;/code&gt; (!) unterwegs sein sollte, wenn ich ein paar mehr Löcher mit dem Bohrer hätte schaffen wollen.&lt;/p&gt;&#10;&lt;h3 id="die-lösung"&gt;Die Lösung&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-12-22-slots.avif" alt="Image: Slot milling solves the problem as I can use a carbide endmill now" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich wechsle also auf Hartmetallfräser. Ich habe nur einen in meiner Sammlung, der für Stahl gemacht wurde. Dieser hat allerdings einen größeren Durchmesser, sodass ich mein Design anpassen muss.&lt;/p&gt;&#10;&lt;p&gt;Nun erzeuge ich mir im CAD Langlöcher, welche mit &lt;code&gt;6mm&lt;/code&gt; Breite und &lt;code&gt;22mm&lt;/code&gt; Länge im &lt;code&gt;45°&lt;/code&gt;-Winkel über dem Radiator der Grafikkarte platziert sind.&lt;/p&gt;&#10;&lt;h2 id="werkzeug2-6mm-vhm-fräser"&gt;Werkzeug2: 6mm VHM-Fräser&lt;/h2&gt;&#10;&lt;p&gt;Ich wende meine bereits &lt;a href="https://blog.schallbert.de/milling-steel/"&gt;bewährte Frästechnik&lt;/a&gt; für SECC-Stahl an und verfeinere sie weiter: Hohe Spindeldrehzahl, großer Vorschub, geringe Tiefenzustellung.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;6mm VHM-Fräser, AlTiN-beschichtet, 2 Schneiden, 0.2mm 45°-Fase: S10600 F880 Z-0.18&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h3 id="bearbeitungsvorgang"&gt;Bearbeitungsvorgang&lt;/h3&gt;&#10;&lt;p&gt;Die Maschine arbeitet ruhig und das Werkzeug ist nach Fertigstellung, die knapp zehn Minuten benötigt, nur handwarm. Beim Eintauchen wird sie kurz lauter. Das nächste mal werde ich die Eintauchrampe noch flacher einstellen - etwa auf &lt;code&gt;5°&lt;/code&gt;. Damit verspreche ich mir, die untere Schneidfläche zu schonen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/hoqrsaBrW2nrjszyJTPEpC" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Cutting SECC steel of my PC case&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="schnittqualität"&gt;Schnittqualität&lt;/h3&gt;&#10;&lt;p&gt;Die Schnittkanten sind gratfrei, hochglänzend und messerscharf. Daher ist eine Fase anzubringen. Dies mache ich von Hand mit Ziehklinge und Cuttermesser, was ich schnell bereue: Ich bekomme das nicht so sauber und gleichmäßig hin wie gewünscht.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-12-22-slots-complete.avif" alt="Image: SECC surface quality after milling &amp;amp; de-burr" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Nicht so schlimm: Ich erhalte ein ordentlich gearbeitetes Gehäuse mit deutlich verbesserter Kühlung praktisch zum Nulltarif (Wenn man vom Lehrgeld absieht). In der Abbildung oben ist der Radiator der Grafikkarte klar durch die neuen Schlitze im Gehäuse zu erkennen.&lt;/p&gt;&#10;</description></item><item><title>Jekyll-dockerimage: Bundler und Gemfile</title><link>https://blog.schallbert.de/bundler-ci-gemfile-issue/</link><pubDate>Fri, 14 Nov 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/bundler-ci-gemfile-issue/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-11-15-bundler-errors.avif"&#10; class="post-cover"&#10; alt="Image: bundler logo with error overlay"&#10; title="Jekyll-dockerimage: Bundler und Gemfile" /&gt;&#10;&lt;h2 id="was-ist-bundler"&gt;Was ist &lt;em&gt;bundler&lt;/em&gt;?&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://bundler.io/guides/faq.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;em&gt;Bundler&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist ein Werkzeug, mit dem Abhängigkeiten zwischen Modulen und Bibliotheken in der Programmiersprache &lt;a href="https://www.ruby-lang.org/en/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;em&gt;Ruby&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; verwaltet und versioniert werden können. Ich benutze &lt;em&gt;Bundler&lt;/em&gt;-Kommandos regelmäßig, z.B. um meinen Blog zu bauen und auf dem Server live zu stellen.&lt;/p&gt;&#10;&lt;p&gt;Will ich lokal bauen, so tippe ich beispielsweise&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bundle exec jekyll serve --incremental --future&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;in die Konsole. Dieser Befehl sagt &lt;em&gt;Bundler&lt;/em&gt;, es soll die Applikation &lt;em&gt;jekyll&lt;/em&gt; im Server-Modus ausführen und ihr Parameter mitgeben, welche &lt;em&gt;jekyll&lt;/em&gt; nicht bei jeder Dateiänderung komplett neu bauen lässt und noch nicht veröffentlichte Seiten dennoch mit erstellt.&lt;/p&gt;&#10;&lt;h2 id="bundler-in-der-ci-pipeline"&gt;&lt;em&gt;bundler&lt;/em&gt; in der CI-Pipeline&lt;/h2&gt;&#10;&lt;p&gt;Auch für die Veröffentlichung meiner Seite nutze ich &lt;em&gt;Bundler&lt;/em&gt; als Teil eines Jekyll-Dockerimages, wie bereits öfters auf meiner Seite verlinkt, z.B. bei der Umstellung auf &lt;a href="https://blog.schallbert.de/projects/move-blog-to-own-server/"&gt;self-hosted&lt;/a&gt;. Seit ich auf eine neue Version der Blog-Software umgestiegen bin, sehe ich in meiner CI-Pipeline rätselhafte Fehler. Lokal baut das System jedoch einwandfrei. Wie man solche Fehler angeht und behebt, habe ich hier festgehalten.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;There was an error while trying to write to /path/to/Gemfile.lock&amp;rdquo; - CI console output&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h3 id="keine-schreibrechte"&gt;Keine Schreibrechte&lt;/h3&gt;&#10;&lt;p&gt;&lt;em&gt;Bundler&lt;/em&gt; selbst kann auf meiner CI wie der &lt;em&gt;Jekyll&lt;/em&gt;-user auch nur Dateien lesen und nicht schreiben. Was ich für den &lt;em&gt;Jekyll&lt;/em&gt; output mit &lt;code&gt;chown&lt;/code&gt; beheben kann, möchte ich &lt;em&gt;bundler&lt;/em&gt; aber gar nicht erlauben: Ich will ja gerade, dass &lt;code&gt;Gemfile.lock&lt;/code&gt; zwischen meiner lokalen Bauumgebung und der CI identisch bleibt, damit ich mögliche Fehler schon vorab beheben kann.&lt;/p&gt;&#10;&lt;h3 id="spezifikationsdatei-für-bundler"&gt;Spezifikationsdatei für &lt;em&gt;bundler&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;In der Datei &lt;code&gt;Gemfile.lock&lt;/code&gt; stehen alle Abhängigkeiten der verwendeten Applikation drin, inkl. Versionsnummer und Bezugsquelle. Wird nur der Quellcode der Anwendung plus diese Datei geliefert, so kann &lt;em&gt;bundler&lt;/em&gt; eine konkrete Version aller Abhängigkeiten beim Bauen ziehen, sodass unabhängig von Maschine und Version mit ähnlichen Voraussetzungen gearbeitet werden kann.&lt;/p&gt;&#10;&lt;h3 id="fehlerbehebung"&gt;Fehlerbehebung&lt;/h3&gt;&#10;&lt;p&gt;Ein erster Versuch der Behebung durch Angleichung sämtlicher Versionen zwischen lokal und CI schlug fehl.&#10;Der zweite Versuch, dem Jekyll-user Schreibrechte auf die &lt;code&gt;Gemfile.lock&lt;/code&gt; zu geben, schlug ebenfalls fehl.&#10;Der dritte Versuch führte mich zur Bundler-Website, wo ich die Parameter genauer unter die Lupe nahm, Stichwort &amp;ldquo;frozen&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Es ist nämlich so, dass man &lt;em&gt;bundler&lt;/em&gt; verbieten kann, die &lt;code&gt;Gemfile.lock&lt;/code&gt; umzuschreiben. Dafür verwendet man das Kommando&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bundle config set frozen true&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Laut &lt;a href="https://bundler.io/v2.7/man/bundle-config.1.html#LIST-OF-AVAILABLE-KEYS" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Dokumentation&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; wird die Verarbeitung abgebrochen, sobald die Datei umgeschrieben werden müsste.&lt;/p&gt;&#10;&lt;p&gt;Obwohl ich diesen Befehl meiner &lt;code&gt;yaml&lt;/code&gt;-Datei in der CI hinzugefügt hatte, schlug das Bauen erneut fehl. Auslöser war dieselbe Bibliothek &lt;a href="https://nokogiri.org/#" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;em&gt;nokogiri&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; wie vorher, nur war die Fehlermeldung nun viel hilfreicher:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-11-14-gemfile-lock-incompatible.avif" alt="Image: jekyll build error due to build platform incompatibility between local and remote." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="gemfilelock-für-verschiedene-umgebungen-optimieren"&gt;&lt;code&gt;Gemfile.lock&lt;/code&gt; für verschiedene Umgebungen optimieren&lt;/h2&gt;&#10;&lt;p&gt;Also befolge ich den Vorschlag und führe das gewünschte Kommando auf meinem lokalen Rechner aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bundle lock --add-platform x86_64_musl &lt;span style="color:#75715e"&gt;# my CI runner&amp;#39;s environment&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Randnotiz: &lt;a href="https://musl.libc.org/about.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;musl&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist eine &lt;em&gt;libc&lt;/em&gt;-Implementierung für Linux, sprich mit ihrer Hilfe laufen viele in &lt;code&gt;C&lt;/code&gt; geschriebene Programme.&lt;/p&gt;&#10;&lt;p&gt;In meiner Lockfile taucht daraufhin folgender Eintrag auf:&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;nokogiri (1.18.10-x86_64-linux-musl)&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Nun läuft meine CI wieder sauber durch und nebenbei habe ich die Portabilität meines Jekll-Setups verbessert.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-11-14-jekyll-yml-freeze-bundler.avif" alt="Image: successful CI run with updated dependencies" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>fail2ban: Update zerstört Konfiguration</title><link>https://blog.schallbert.de/fail2ban-error-configuration-bad-value/</link><pubDate>Thu, 23 Oct 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/fail2ban-error-configuration-bad-value/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-09-23-docker-chain-thumb.avif"&#10; class="post-cover"&#10; alt="Image: iptables view for docker-user chain"&#10; title="fail2ban: Update zerstört Konfiguration" /&gt;&#10;&lt;p&gt;Alle paar Wochen sehe ich auf meinem Server üblicherweise nach dem Rechten:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Ist bei Docker alles ok? &lt;code&gt;docker ps&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Werden die Backups korrekt erzeugt? &lt;code&gt;borgmatic repo-info&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Funktionieren meine Abwehrmaßnahmen? &lt;code&gt;iptables -n -L&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Laufen meine Daemons zur Update-Automation? &lt;code&gt;htop -F /server-config&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Dieses mal allerdings zeigte &lt;code&gt;iptables&lt;/code&gt; mir gähnende Leere. Das ist ungewöhnlich und deutet darauf hin, dass mit &lt;em&gt;fail2ban&lt;/em&gt; etwas nicht stimmt.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# iptables -n -L&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Chain FORWARD &lt;span style="color:#f92672"&gt;(&lt;/span&gt;policy DROP&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;target prot opt source destination &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DOCKER-USER all -- 0.0.0.0/0 0.0.0.0/0 &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DOCKER-FORWARD all -- 0.0.0.0/0 0.0.0.0/0 &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Chain DOCKER-USER &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; references&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;target port opt source destination &#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Übersetzt lautet die Ausgabe etwa in etwa&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Die &lt;code&gt;FORWARD&lt;/code&gt;-Chain leitet wie von &lt;em&gt;Docker&lt;/em&gt; benötigt an die für Container relevante &lt;code&gt;DOCKER-USER&lt;/code&gt;-Chain weiter. Unter dieser sind allerdings keine Anwendungen gelistet, die Pakete zurückweisen oder deren Weiterleitung verbieten (&lt;code&gt;REJECT/DROP&lt;/code&gt;) können.&amp;rdquo;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;An dieser Stelle müssten zwei Einträge für &lt;code&gt;caddy-server&lt;/code&gt; und &lt;code&gt;caddy-ratelimit&lt;/code&gt; auftauchen.&lt;/p&gt;&#10;&lt;h2 id="fail2ban-error-failed-during-configuration"&gt;&amp;ldquo;fail2ban ERROR Failed during configuration&amp;rdquo;&lt;/h2&gt;&#10;&lt;p&gt;Also schaue ich zuerst mal bei Docker nach:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker logs -t fail2ban &lt;span style="color:#75715e"&gt;# Logs für den Container mit Namen fail2ban mit Zeitempel ausgeben&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Statt wie üblich nach dem Durchscrollen mit &lt;code&gt;&amp;lt;timestamp&amp;gt; Server ready&lt;/code&gt; belohnt zu werden, bekomme eine nicht enden wollende Flut an Logmeldungen angezeigt. Sie wiederholen sich und zeigen, dass &lt;em&gt;fail2ban&lt;/em&gt; mit &lt;code&gt;Exit with code 255&lt;/code&gt; aussteigt. Einen besseren Überblick liefert mir das Log, wenn ich nur die letzten 100 Zeilen ausgebe:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker logs -t --tail &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt; fail2ban &lt;span style="color:#75715e"&gt;# Nur die letzten 100 Zeilen des Logs ausgeben, mit Zeitstempel&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="die-fehlermeldung"&gt;Die Fehlermeldung&lt;/h3&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;&amp;lt;timestamp, ID&amp;gt; ERROR Failed during configuration: Bad value substitution: &#10;option &amp;#39;action&amp;#39; in section &amp;#39;caddy-ratelimit&amp;#39; contains an interpolation key &amp;#39;banaction&amp;#39; which is not a valid option name. &#10;Raw value: &amp;#39;%(action_)s&amp;#39;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Die Meldung ist interessant, weil ich genau an dieser Stelle auch schon beim Erstellen im Zuge des Artikels &lt;a href="https://blog.schallbert.de/fail2ban-with-caddy/#check-jail"&gt;Fail2ban mit Caddy&lt;/a&gt; Probleme mit der &lt;code&gt;jail.local&lt;/code&gt; hatte. Damals musste ich &lt;code&gt;action = iptables-multiport&lt;/code&gt; aus der Konfiguration nehmen, weil die auslösenden IP-Adressen auf der falschen &lt;code&gt;CHAIN&lt;/code&gt; (INPUT) gelandet waren.&lt;/p&gt;&#10;&lt;p&gt;Nun scheint also die default-action auf der &lt;code&gt;DOCKER-USER&lt;/code&gt; chain nicht mehr zu funktionieren. Also gehe ich ins Netz und suche nach ein paar Quellen für eine mögliche Lösung. Ich finde aber weder in der Update-Historie von &lt;em&gt;fail2ban&lt;/em&gt; noch in der von mir benutzten Distribution von &lt;a href="https://docs.linuxserver.io/images/docker-fail2ban/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;linuxserver.io&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Hinweise auf Updates, die hier große Änderungen im &lt;code&gt;action.d&lt;/code&gt; Ordner veranlassen würden.&lt;/p&gt;&#10;&lt;h3 id="lösung-per-banaction--iptablestypemultiport"&gt;Lösung per &lt;code&gt;banaction = iptables[type=multiport]&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;Also bemühe ich die Websuche und finde einen Kommentar eines &lt;a href="https://github.com/fail2ban/fail2ban/issues/3894" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Maintainers von fail2ban&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;[&amp;hellip;] With action = iptables[type=multiport, protocol=tcp, chain=DOCKER-USER] you overwrote the default action [&amp;hellip;] [Solution:] simply set the chain variable only in the jail [&amp;hellip;]: &lt;code&gt;port = http, https banaction = iptables[type=multiport] chain = DOCKER-USER&lt;/code&gt;&amp;rdquo;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Die Grundlage dieser Aussage kann man in den &lt;a href="https://docs.docker.com/engine/network/packet-filtering-firewalls/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Docker Docs&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; nachlesen. Nebenbemerkung: An allen Stellen wird empfohlen, für zukünftige Anwendungen das modernere &lt;em&gt;nftables&lt;/em&gt; statt &lt;em&gt;iptables&lt;/em&gt; zu verwenden. &lt;a href="https://linux-audit.com/networking/nftables/differences-between-iptables-and-nftables-explained/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Hier&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; gibt es einen Artikel zum Thema.&lt;/p&gt;&#10;&lt;p&gt;Somit kann ich das bisher in meiner &lt;code&gt;jail.local&lt;/code&gt; vorhandene &lt;code&gt;action&lt;/code&gt;-Feld komplett löschen und stattdessen &lt;code&gt;banaction&lt;/code&gt; verwenden. Sie sieht nun wie folgt aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# jail.local update Oct-2025&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Server/Client errors: 401 403 404 500&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#ae81ff"&gt;caddy-status]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;enabled = true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;chain = DOCKER-USER&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;port = http,https&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;filter = caddy-status&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;logpath = &amp;lt;path/to/logfiles&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;findtime = 10min&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;maxretry = 5&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;bantime = 1d&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;banaction = iptables[type=multiport]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mit dieser Konfiguration fährt &lt;em&gt;fail2ban&lt;/em&gt; normal hoch und gibt &lt;code&gt;Server ready&lt;/code&gt; aus.&lt;/p&gt;&#10;&lt;h3 id="überprüfen-der-lösung-mittels-iptables"&gt;Überprüfen der Lösung mittels &lt;em&gt;iptables&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;Ich erzeuge mir wie oben beschrieben Einträge in den Test-Logfiles, welche die Aktionen von &lt;code&gt;caddy-status&lt;/code&gt; und &lt;code&gt;caddy-ratelimit&lt;/code&gt; auslösen sollen. Wie erwartet erhalte ich in der &amp;ldquo;blocklist&amp;rdquo;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# iptables -n -L&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Chain DOCKER-USER &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; references&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;target prot opt source destination &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;f2b-caddy-ratelimit tcp -- 0.0.0.0/0 0.0.0.0/0 multiport dports 80,443&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;f2b-caddy-status tcp -- 0.0.0.0/0 0.0.0.0/0 multiport dports 80,443&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Albasia-Holz fräsen</title><link>https://blog.schallbert.de/albasia-on-cnc/</link><pubDate>Mon, 06 Oct 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/albasia-on-cnc/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-albasia-on-cnc-thumb.avif"&#10; class="post-cover"&#10; alt="Image: Macro shot of Albasia plywood, layer view"&#10; title="Albasia-Holz fräsen" /&gt;&#10;&lt;h2 id="was-ist-albasia"&gt;Was ist Albasia?&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-albasia-samplecolors.avif" alt="Image: Albasia sandwich material samples with coloured melamine surface" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Albasia (&lt;a href="https://en.wikipedia.org/wiki/Falcataria_falcata" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Falcataria falcata&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) ist ein sehr leichtes Weichholz. Es kommt hauptsächlich in &lt;a href="https://www.plywoodworking.com/exploring-albasia-wood-a-sustainable-solution-for-the-timber-industry.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Südostasien&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; vor und wächst schnell, was sein &lt;a href="https://www.juergensen.de/news/albasia-sperrholz-unsere-gruene-loesung-fuer-die-industrie/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Nachhaltigkeitsrating&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; verbessert. Seine Farbe variiert zwischen weißlich-gelb und pastelligem braun-rosa, ist im Vergleich zu Buche oder Eiche aber deutlich heller.&lt;/p&gt;&#10;&lt;p&gt;Hierzulande ist es meist als Multiplexplatte im Handel. Dadurch werden Nachteile wie die starke Neigung zum &lt;a href="https://www.holzplatten-shop.de/media/pdf/2e/97/f3/Das-Schwinden-und-Quellen-von-Holz.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Schwinden und Quellen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; abgemildert.&lt;/p&gt;&#10;&lt;p&gt;Die Dichte von Albasia ist nochmal etwa 15% geringer als die des ohnehin schon sehr leichten Pappelholzes. Es bildet im Wuchs lange Fasern, was die Zerspanbarkeit beeinträchtigt. Da es durch seine vielen Lufteinschlüsse zudem gut dämmt, wird es häufig im Fahrzeugausbau (Camper, Boote etc.) verwendet. Um die geringere Festigkeit auszugleichen, wird Albasia-Multiplex oft nicht roh, sondern als Sandwich-Platte angeboten.&lt;/p&gt;&#10;&lt;h3 id="mit-sandwichtechnik-zu-guten-materialeigenschaften"&gt;Mit Sandwichtechnik zu guten Materialeigenschaften&lt;/h3&gt;&#10;&lt;p&gt;Mein Testmaterial verfügt über eine beidseitig aufgebrachte, &lt;code&gt;0.8mm&lt;/code&gt; starke Schicht aus Hochdrucklaminat (HPL). Die Oberfläche ist in farbigen Melamindekoren erhältlich und kann als Extra gehärtet bestellt werden, was sie mattiert sowie unempfindlich gegenüber Kratzern macht.&lt;/p&gt;&#10;&lt;h3 id="sind-albasia-verbundplatten-für-den-außenbereich-geeignet"&gt;Sind Albasia-Verbundplatten für den Außenbereich geeignet?&lt;/h3&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Unsere Platten sind übrigens nicht für den Außenbereich vorgesehen.&amp;rdquo; - EpicPLY, Händler&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Auch wenn das Material für den Innenausbau entwickelt wurde: Ich teste die Wetterfestigkeit der Platten momentan mit einem Experiment. Dafür verschraube ich die Verbundplatte im rechten Winkel auf Stoß miteinander, sodass einmal die Beschichtung und einmal die Holzkante der Witterung direkt ausgesetzt sind. Ich habe eine offene Kante mit Hartöl versiegelt und eine unbehandelt belassen. Nun hänge ich die Konstruktion ein Jahr draußen auf den Balkon und sehe dann, wie sich das Material verhält - Update dann in einem gesonderten Post!&lt;/p&gt;&#10;&lt;section class="hugo-gallery"&gt;&#10; &lt;div class="hugo-gallery__frame"&gt;&#10; &lt;div class="hugo-gallery__grid" role="list"&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-0" aria-label="Image: test material and tools: plywood sample, srews, drills"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-albasia-outdoor-test-setup.avif" alt="Image: test material and tools: plywood sample, srews, drills" class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-1" aria-label="Image: Albasia weather test mounted outside"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-albasia-outdoor-test-mount.avif" alt="Image: Albasia weather test mounted outside" class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-2" aria-label="Image: Comparison of oiled and raw plywood surface on day0 of test. Both look fresh and new."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-albasia-outdoor-test-surface.avif" alt="Image: Comparison of oiled and raw plywood surface on day0 of test. Both look fresh and new." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;/div&gt;&lt;div class="hugo-gallery__caption"&gt;Versuchsaufbau für Bewetterungs- und Schraubentest.&lt;/div&gt;&lt;/div&gt;&#10;&lt;/section&gt;&#10;&#10; &lt;div class="hugo-gallery__full" aria-live="polite"&gt;&lt;figure id="gallery-full-0" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-albasia-outdoor-test-setup.avif" alt="Image: test material and tools: plywood sample, srews, drills" class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Vorbereitung für den Schraubentest&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-1" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-albasia-outdoor-test-mount.avif" alt="Image: Albasia weather test mounted outside" class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Der zusammengesetzte Testaufbau ist zur Bewitterung angebracht.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-2" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-albasia-outdoor-test-surface.avif" alt="Image: Comparison of oiled and raw plywood surface on day0 of test. Both look fresh and new." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Am Tag 0: Beide Oberflächen sehen noch frisch aus.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;/section&gt;&#10;&lt;p&gt;Meine Erwartung: anders als bei Birkensperrholz und ähnlich wie Pappelholz wird Albasia nicht für den Außenbereich taugen. Ich nehme an, dass sich die Fasern im Kantenbereich mit Wasser vollsaugen und das Material zum Quellen bringen. Dies würde es schnell verrotten lassen. Außerdem schätze ich die Gefahr für Insektenbefall als hoch ein, weil die Fasern echt weich sind und sich leicht abschälen lassen. Daher würde ich es nur in Innenräumen einsetzen.&lt;/p&gt;&#10;&lt;h3 id="verschraubungen"&gt;Verschraubungen&lt;/h3&gt;&#10;&lt;p&gt;Trotz seines leichten Kerns sei die Albasia-Verbundplatte gut zu verschrauben und würde mit einer &amp;ldquo;Schraubenauszugsfestigkeit von &lt;code&gt;120kg&lt;/code&gt;&amp;rdquo; bei einer &lt;code&gt;4mm-Holzschraube&lt;/code&gt; in einem &amp;ldquo;sehr stabilen Bereich&amp;rdquo; liegen, meint mein Händler.&lt;/p&gt;&#10;&lt;p&gt;Als Laie kann ich mit solchen Werten nicht viel anfangen und führe ein kleines Experiment durch. Ich nehme eine &lt;code&gt;4x35&lt;/code&gt; Holzschraube her und schaue mir die vorgesehenen Anzugsmomente an. Hier gilt für meinen Hersteller etwa:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;Parameter&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;Wert&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Auszugsparameter \( f_{ax,k} \)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;14 N/mm²&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Torsionsfestigkeit \( f_{tor,k} \)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3 Nm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Einschraubdrehmoment \( R_{tor,mean} \)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;\( \frac{f_{tor,k}}{R_{tor,mean}} \) ≥ 1,5&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Vorbohren in Nadelholz&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2.5 mm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Vorbohren in Laubholz&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3 mm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;a href="https://www.spax.com/de-de/p/universalschraube-teilgewinde-senkkopf-t-star-plus-4cut-wirox.html?variant=0191010400353" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Quelle&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Nun schaue ich, ob ich zwei Bretter auf Stoß mit dem vorgesehenen Anzugsmoment verbinden kann. Dafür nehme ich einen Drehmomentschraubendreher zur Hand. Doch ich komme gar nicht bis zum Auslösen der Mechanik: Bereits vorher beginnt die Schraube, sich in die Deckschicht zu ziehen. Das maxinale Drehmoment, bevor die Schraube im Material &amp;ldquo;verschwindet&amp;rdquo;, schätze ich auf lediglich &lt;code&gt;0.8Nm&lt;/code&gt;.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2025-10-06-albasia-fastener-test.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Video: Ich versuche, eine Schraube auf Drehmoment im Holz festzuziehen.&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Somit lassen sich durchaus stabile Schraubverbindungen herstellen, jedoch würde ich beim Anziehen stets vorsichtig vorgehen, großzügig mit der Menge an Schrauben sein und zusätzlich Dübel- oder formschlüssige Verbindungsarten wählen.&lt;/p&gt;&#10;&lt;h2 id="albasia-auf-der-cnc-fräse"&gt;Albasia auf der CNC-Fräse&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-6mm-schlicht.avif" alt="Image: fine-cutting Albasia with a 6mm carbide endmill" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Albasia kann wie jedes andere Weichholz auch mit der CNC-Fräse verarbeitet werden. Für Weichhölzer typische Probleme wie Ausrisse oder Fasernester sind auch hier zu erwarten. Hinzu kommt die harte Beschichtung, welche die Bearbeitung wegen anderer Anforderungen an die Parameterwahl noch einmal schwieriger macht.&lt;/p&gt;&#10;&lt;h3 id="mit-welchen-fräsern-gelingt-die-bearbeitung"&gt;Mit welchen Fräsern gelingt die Bearbeitung?&lt;/h3&gt;&#10;&lt;p&gt;Für den Fräser ist der Materialmix herausfordernd. Beim Fräsen in HPL hatte ich im Artikel &lt;a href="https://blog.schallbert.de/cnc-router-overload/"&gt;zur Überlastung meines Fräsmotors&lt;/a&gt; ja bereits schlechte Erfahrungen gesammelt. Das Material damals besaß im Vergleich zu Albasia-Verbund jedoch einen viel höheren Hartpapierfaseranteil (Späne sind hellbraun und mit vielen &amp;ldquo;Punkten&amp;rdquo; durchzogen), was den Fräser in Rekordzeit stumpf werden ließ.&lt;/p&gt;&#10;&lt;p&gt;Ich setze Vollhartmetallfräser &lt;code&gt;VHM&lt;/code&gt; mit Spanbrecher und kaum Drall (&lt;code&gt;15°&lt;/code&gt;) ein, um die langen Holzfasern zu brechen. Sie verstopfen sonst sehr schnell die Absaugung und sammeln sich um den Fräser an, was zu Brandgefahr führen kann.&lt;/p&gt;&#10;&lt;p&gt;Für die Kantenbearbeitung habe ich mit komprimierenden Fräsern wie dem Viertelstab gute Erfahrungen gemacht. Taucht man nur leicht in das Material ein, so eignet sich aber auch ein V-Cut. Bei größerer Eintauchtiefe erreichen Halbrundfräser &amp;ldquo;Ball-nose&amp;rdquo; ein schönes Fräsbild.&lt;/p&gt;&#10;&lt;p&gt;Auf dem Bild ist ein Vergleich dargestellt. Rechts mit grün-grauer Beschichtung das Rohmaterial, geschnitten an einer Formatkreissäge. Links in violett ist das von mir CNC-gefräste Werkstück zu sehen. Das Schnittbild unterscheidet sich deutlich voneinander und die mit dem Fräser bearbeitete Fläche ist viel sauberer und ohne Ausrisse.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-albasia-mill-vs-saw.avif" alt="Image: Albasia / Albizia plywood sandwiched between HPL coatings. Saw cut reveals to be much more coarse with pull-outs versus the milled surface." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Geht man in der Vollnut mit spanbrechenden Fräsern vor, lohnt sich ein Schlichtgang mit gerade gezahntem Fräser und großen Vorschüben bei nur geringer Abnahme von &lt;code&gt;0.2mm&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Allen Fräsern gemein sein muss&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;eine möglichst scharfe Schneide (keine Beschichtung!)&lt;/li&gt;&#10;&lt;li&gt;eine große Schnittgeschwindigkeit (keine zu kleinen Durchmesser verwenden)&lt;/li&gt;&#10;&lt;li&gt;ein verhältnismäßig großer Vorschub.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Mit Einschneidern habe ich eine hohe Oberflächengüte erzielen können. Der große Spankanal sorgt für gute Abfuhr, allerdings sind Ausrisse bei der Arbeit im Vollen mitunter problematisch. Die Schneidlast ist recht hoch, und die Schneiden sind im harten Obermaterial nicht dauerhaltbar. Für Schlichtgänge allerdings ideal.&lt;/p&gt;&#10;&lt;h3 id="welche-parameter"&gt;Welche Parameter?&lt;/h3&gt;&#10;&lt;p&gt;Albasia ist so weich, dass es den Achsantrieben der Fräse kaum Widerstand bietet. Die Schnittgeschwindigkeit ist hoch anzusetzen, Quellen im Internet empfehlen etwa &lt;code&gt;500m/min&lt;/code&gt; als Richtwert. Als Vorschub habe ich Werte bis &lt;code&gt;6000mmm/min&lt;/code&gt; ausprobiert. Ich habe aber auch nur kleine Werkstücke bearbeitet, weswegen noch höhere Vorschübe wegen der ständigen Richtungswechsel kaum einen Zeitvorteil böten. Ein zusätzlicher Schlichtgang ist wegen der Marken des Spanbrechers Pflicht.&lt;/p&gt;&#10;&lt;p&gt;Einen guten Kompromiss zwischen Fräszeit und Oberflächenqualität habe ich für verschiedene Fräser unten festgehalten. Die Gravierfräser lasse ich allerdings ausschließlich in der HPL-Schicht arbeiten, weswegen ich Vorschub und Drehzahl etwas reduziere.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Werkzeug (s= Anzahl Schneiden)&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Vorschub &lt;code&gt;F&lt;/code&gt; [mm/min]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Drehzahl &lt;code&gt;S&lt;/code&gt; [U/min]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Zustellung Z [mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Zustellung XY [mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;VHM-2s spanbrechend 15° 6mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4200&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;24000&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2.7&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;VHM-2s gerade 6mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3700&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;26500&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;21&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;VHM V-cut 3s 90° 10mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3000&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;21000&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.27&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;VHM Radienfräser 2s 6mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3000&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;21000&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.27&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;VHM Viertelstab 4s r2 6mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3600&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;15000&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="schlichtgang"&gt;Schlichtgang&lt;/h3&gt;&#10;&lt;p&gt;Das folgende Video zeigt die den Schlichtvorgang nach dem Schruppen eines &lt;code&gt;145x145mm&lt;/code&gt; großen Werkstückes. Durch die geringe seitliche Zustellung bilden sich kaum Fasern aus und der geringe Vorschub sorgt trotz leichtem Drall des Fräsers für eine sehr hohe Oberflächengüte.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2025-10-06-albasia-schlicht.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;CNC-Schlichten von Albasia Verbundmaterial&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="gravuren"&gt;Gravuren&lt;/h2&gt;&#10;&lt;p&gt;Bei beschichteten Albasiaplatten finden Gravuren idealerweise in der Deckschicht statt. Wird nicht in das Trägermaterial eingetaucht, so können keine Probleme mit Ausrissen und losen Fasern entstehen. Da die Deckschicht nicht sehr stark ist, fällt die Wahl schnell auf V-Fräser mit stumpfem Winkel oder mit abgeflachter Spitze.&lt;/p&gt;&#10;&lt;p&gt;Alternativ hierzu können Bereiche mit Schaftfräsern vorgeräumt werden, falls das CAM-Programm eine solche Option bereitstellt. So entstehen komplett glatte &amp;ldquo;Böden&amp;rdquo; innerhalb der Taschen. Oder man verwendet Halbrundfräser wegen ihrer nur sanft ansteigenden Geometrie im unteren Bereich. Unterstützt das CAM-Programm die 3D-Gravur hiermit nicht, so kann die Fräsergeometrie wie im Artikel &lt;a href="https://blog.schallbert.de/halftone-image-on-dibond/#simulation-radienfr%c3%a4ser"&gt;Halbtonbilder auf Dibond&lt;/a&gt; beschrieben angenähert werden.&lt;/p&gt;&#10;&lt;p&gt;Zwei Beispiele habe ich im untenstehenden Bild vorbereitet: Der CR-Code in der oberen Hälfte entstand mit einem 6mm-Radienfräser bei &lt;code&gt;0.2mm&lt;/code&gt; Eintauchtiefe. Die entstehenden Punkte und Linien fühlen sich durch die sanften Übergänge weich und angenehm an. Der Schriftzug in der unteren Bildhälfte ist mit einem Gravierfräser und bis zu &lt;code&gt;7mm&lt;/code&gt; Tiefenzustellung gefräst. Hierdurch kommt der natürliche Holzton zwar schön zur Geltung, bei kleinen Schrithöhen wie dieser (max. &lt;code&gt;30mm&lt;/code&gt;) entstehen aber leicht Ausbrüche bei Buchstaben mit &amp;ldquo;Inseln&amp;rdquo; (a, e, b, o, etc.).&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-06-albasia-engrave-compare.avif" alt="Image: Albasia sandwich material engraved. Upper part using a radius endmill not penetrating the HPL cover, yields dark and very clean finish but lacks contrast. Lower part with a classic 60° V-cut with a brighter wooden finish but with artifacts" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="mit-farbe-arbeiten"&gt;Mit Farbe arbeiten&lt;/h2&gt;&#10;&lt;p&gt;Die Kontraste der Gravuren sind zwar in Ordnung, die Lesbarkeit ist aber längst nicht perfekt. Da liegt es nahe, Gravuren mit Farbe auszulegen. Ich habe mit ein paar Farbtönen experimentiert; ein paar Impressionen finden sich im Bild unten.&lt;/p&gt;&#10;&lt;h3 id="welcher-lack"&gt;Welcher Lack?&lt;/h3&gt;&#10;&lt;p&gt;Die Platte wird mit einer Schutzfolie ausgeliefert, um die farbige Beschichtung zu schützen. Das macht das Auslegen mit Farbspray total einfach, wenn ich die Schutzfolie für die Gravurbearbeitung auf dem Werkstück belasse. Ich habe mit handelsüblichem Acryllack auf Wasserbasis (oben rechts), Farbspray mit Metallpartikeln (unten) und mit lösemittelbasiertem Permanent Marker (oben links) gearbeitet.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-10-25-engrave-compare.avif" alt="Image: Impressions of coloured engravings of the name *Schallbert* and the QR-code to this website." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Dabei ging die Lackierung stets leicht von der Hand. Die Abriebfestigkeit ist bei den in HPL gravierten Flächen hoch. Graviert man bis hindurch zum Holz, so stellen sich die Fasern durch das Lackieren etwas auf, was später nur sehr zeitaufwendig korrigiert werden kann und dem Gesamteindruck nicht zuträglich ist.&lt;/p&gt;&#10;&lt;h3 id="zu-beachten-schutzfolie"&gt;Zu beachten: Schutzfolie&lt;/h3&gt;&#10;&lt;p&gt;Der Lackiervorgang sollte möglichst direkt nach dem Gravieren gestartet werden und nicht erst Tage oder gar Wochen später. Ansonsten nämlich neigt die Schutzfolie zum Ablösen bzw. zur Blasenbildung an den Rändern der Gravur und die Farbe wird durch Kapillarkräfte in Bereiche gezogen, wo sie nicht hingehört. Dies hat mir die gold-blaue Version meiner Testreihe leider deutlich sichtbar ruiniert.&lt;/p&gt;&#10;&lt;h2 id="fazit-albasia-für-was"&gt;Fazit: Albasia für was?&lt;/h2&gt;&#10;&lt;p&gt;Alles in Allem würde ich Albasia zu den eher gutmütigen Materialien zur CNC-Bearbeitung zählen, die einen weiten Parameterbereich von Vorschüben bei hoher Schnittqualität akzeptiert. Herausfordernd sind die Neigung zum Bilden langer Fasern und die sehr unterschiedliche Härte zwischen Deckschicht und Kernmaterial; Geht man mit zu hohen Vorschüben vor, so nutzt der Fräser in den Bereichen mir HPL-Deckschicht sehr schnell ab.&lt;/p&gt;&#10;&lt;p&gt;Als Gravurmaterial eignet es sich ebenfalls, wofür aber die Beschichtung allein bereits genügen würde und durch die kleinen Gravurtiefen enge Grenzen für die Strichbreite gesetzt sind. Die Gravur kann also allenfalls nettes Beiwerk zum eigentlichen Einsatz der Platten sein.&lt;/p&gt;&#10;&lt;p&gt;Die Beschichtung macht das Material absolut möbelbaufähig. Arbeitet man mit verdeckten Kanten, so kann ich mir sogar den Einsatz in der Küche vorstellen. Da es eine hohe Biegestabilität besitzt und die Beschichtung mit hoher Härte besticht, ist es sogar als Oberfläche für den täglichen Gebrauch verwendbar. Das Plattenmaterial ist federleicht und eignet sich daher auch prima für mobile Anwendungen im Sichtbereich.&lt;/p&gt;&#10;&lt;p&gt;An die Robustheit von Birke Multiplex reicht es jedoch nicht heran. Belastete Verbindungen traue ich dem Material ohne Weiteres nicht zu; bei Verschraubungen würde ich Oberflächen maximieren und stets Möbelbauverbindungen einsetzen (also keine Holzschrauben nehmen).&lt;/p&gt;&#10;</description></item><item><title>passtdas - ein Hilfsprogramm für die CNC</title><link>https://blog.schallbert.de/passtdas/</link><pubDate>Mon, 08 Sep 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/passtdas/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-09-08-passtdas-thumb.avif"&#10; class="post-cover"&#10; alt="Image: Visualizazion of how passtdas analyzes and determines workpiece edges"&#10; title="passtdas - ein Hilfsprogramm für die CNC" /&gt;&#10;&lt;h2 id="das-problem"&gt;Das Problem&lt;/h2&gt;&#10;&lt;p&gt;Ein Bekannter von mir schrieb mich an. Er betreibt eine CNC-Portalfräsmaschine als Hobby so wie ich und fertigt oft Einzelstücke. Die dabei entstehenden Reste sind aber teils zu schade zum Wegwerfen. Für eine bestmögliche Materialausnutzung probiert er, diese Reste als Rohlinge für weitere Projekte zu verwenden.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Weißt du, wie ich an der Maschine herausfinden kann, ob mein Frästeil auf den Rohling passt?&amp;rdquo; - Anonym&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Schnell ist es passiert: Den Maßen zufolge passt das Werkstück so gerade auf den Rohling. Setzt man den Nullpunkt nur leicht falsch, so fährt der Fräser über die Werkstückgrenzen hinaus und man kann das Werkstück wegwerfen.&lt;/p&gt;&#10;&lt;h2 id="die-lösung"&gt;Die Lösung&lt;/h2&gt;&#10;&lt;p&gt;Es wäre doch toll, wenn die Maschine vor Beginn des Fräsvorganges die Kontur des Werkstückes abfahren würde, oder? So könnte man unkompliziert herausfinden, ob sich der Rohling wirklich komplett innerhalb der Werkstückgrenze befindet.&lt;/p&gt;&#10;&lt;p&gt;Da ich auf Anhieb keine schnelle Lösung online finden kann, denke ich an selbstgeschriebene Software für die Umsetzung. Daher mache ich mich an die Arbeit.&lt;/p&gt;&#10;&lt;p&gt;TODO: ADD VIDEO LINK&lt;/p&gt;&#10;&lt;h2 id="die-software"&gt;Die Software&lt;/h2&gt;&#10;&lt;p&gt;Für einen schnellen Prototypen wähle ich die Hochsprache &lt;a href="https://www.python.org/downloads/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;python&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Warum? Ich beherrsche &lt;em&gt;python&lt;/em&gt; recht gut, Bibliotheken für die erforderlichen geometrischen Berechnungen sind verfügbar, und das geschriebene Programm lässt sich leicht in eine all-in-one-Datei kompilieren.&lt;/p&gt;&#10;&lt;p&gt;Das Programm soll Maschineninstruktionen (G-Code) lesen und die Fräspfade analysieren. Die Stellen, an denen im Fräsprogramm die maximalen und minimalen &lt;code&gt;X- und Y-Werte&lt;/code&gt; angefahren werden, sollen abgespeichert und selbst als Bewegungskommando in einer Pfaddatei ausgegeben werden.&lt;/p&gt;&#10;&lt;p&gt;Lädt man dieses Programm auf die Maschine, so wird ein so ermittelter &amp;ldquo;Extremwert&amp;rdquo; angefahren und die Z-Achse von Sicherheits- auf Messhöhe abgesenkt. Anschließend wird das Programm pausiert, sodass der Bediener Anpassungen der Werkstückposition vornehmen kann. Erst durch Einabe eines weiteren Startbefehls wird der nächste Extremwert angefahren usw.&lt;/p&gt;&#10;&lt;p&gt;Dieses Programm wird &lt;em&gt;passtdas&lt;/em&gt; heißen.&lt;/p&gt;&#10;&lt;h3 id="programmart"&gt;Programmart&lt;/h3&gt;&#10;&lt;p&gt;Ich entscheide mich für ein Konsolenprogramm mit wenigen Parametern:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Shortcut&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Parameter name&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Description&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;-f&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;--file&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The source file to be analyzed. Required.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;-s&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;--zsafety&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Safety height the machine will do flyovers with. Optional.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;-p&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;--zprobe&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Probe height the machine will pinpoint to. Optional.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Die beiden Höhenparameter bekommen Default-Werte, sodass sie nicht immer mit angegeben werden müssen.&lt;/p&gt;&#10;&lt;h3 id="eingabe"&gt;Eingabe&lt;/h3&gt;&#10;&lt;p&gt;&lt;em&gt;passtdas&lt;/em&gt; liest Dateien ein und durchsucht sie zeilenweise nach Bewegungsbefehlen &lt;code&gt;G00, G01, G02, G03&lt;/code&gt;. Während die Auswertung für lineare Bewegungen wie &lt;code&gt;G00, G01&lt;/code&gt; einfach ist - Lineare Bewegungen lassen sich durch Punkt-zu-Punkt-Verbindungen ausdrücken, deren Extremwerte nicht zwischen den Punkten liegen können - kann das für Kreissegmente durchaus herausfordernd sein.&lt;/p&gt;&#10;&lt;p&gt;Die Höheninformation auf der Z-Achse wird ebenfalls mitgelesen und gespeichert. So kann die Zerspanungstechnikerin schnell prüfen, ob die maximale Frästiefe mit der Gewünschten übereinstimmt.&lt;/p&gt;&#10;&lt;p&gt;Befinden sich in der G-Code-Datei keine oder falsch formulierte Anweisungen, so gibt das &lt;em&gt;passtdas&lt;/em&gt; entsprechende Fehlermeldungen aus und bricht die Analyse ab.&lt;/p&gt;&#10;&lt;h3 id="definition-kreissegmente"&gt;Definition Kreissegmente&lt;/h3&gt;&#10;&lt;p&gt;Zum Hintergrund: Im G-Code können Kreissegmente auf zwei Arten definiert werden.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;...&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 X5.660 Y-0.000 Z10.000&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G02 X-5.660 Y0.000 R5.660 F1600&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;...&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Kommando:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Fahre einen Kreisbogen im Uhrzeigersinn ab dem Punkt &lt;code&gt;X5.66 Y0&lt;/code&gt; mit Radius &lt;code&gt;5.66mm&lt;/code&gt; bis zum Punkt &lt;code&gt;X-5.66 Y0&lt;/code&gt; mit Vorschub &lt;code&gt;1600mm/min&lt;/code&gt; auf Höhe &lt;code&gt;10mm&lt;/code&gt;.&amp;rdquo;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Zeichnet man diese Kommandos, entsteht ein hängender Halbkreis. Kreise können mittels dreier Punkte definiert werden. Doch nur zwei Punkte sind gegeben. Der dritte Punkt muss mit Hilfe von Anfangs- und Endpunkt aus dem ebenfalls angegebenen Kreisradius berechnet werden.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-09-08-arcr.avif" alt="Image: drawing an arc using given radius, start- and endpoint" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;&lt;em&gt;Aufgabe: Finde heraus, wo dieser Kreis seine Extremwerte in XY hat.&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;Aber auch so kann ein Kreissegment definiert sein:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;...&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 X-2.5696 Y5.0431&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G03 X-4.6258 Y3.2616 Z-0.5000 I2.5696 J-5.0431&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;...&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Kommando:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Fahre einen Kreisbogen gegen den Uhrzeigersinn ab dem Punkt &lt;code&gt;X-2.56 Y5.04&lt;/code&gt; mit Mittelpunktkoordinaten &lt;code&gt;I2.56 J-5.04&lt;/code&gt; bis zum Punkt &lt;code&gt;X-4.62 Y3.26&lt;/code&gt;.&amp;rdquo;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Zeichnet man diese Kommandos, entsteht ein kurzes Kreissegment im oberen linken Quadranten. Hier sind zwar drei Punkte (Start- und Endpunkt sowie Kreismittelpunkt) gegeben. Doch auch hier kann es mitunter schwierig werden, herauszufinden, ob irgendwo auf dem Kreisbogen ein Extremwert angefahren wird.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-09-08-arcij.avif" alt="Image: drawing an arc using center point, start- and endpoint" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;&lt;em&gt;Aufgabe: Finde heraus, wo dieser Kreis seine Extremwerte in XY hat.&lt;/em&gt;&lt;/p&gt;&#10;&lt;h3 id="extremwerte-von-kreissegmenten-berechnen"&gt;Extremwerte von Kreissegmenten berechnen&lt;/h3&gt;&#10;&lt;p&gt;Ein Kreissegment kann je nach Spanne bis zu vier zusätzliche Extremwerte haben: &lt;code&gt;X+, Y+, X-, Y-&lt;/code&gt;. Bei einem Vollkreis sind dies stets die Koordinatenschnittpunkte vom Radius aus gesehen.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_extremes_from_arc&lt;/span&gt;(arc, coordinates):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; x_plus_radius &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [center_x &lt;span style="color:#f92672"&gt;+&lt;/span&gt; radius, center_y, xyz[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;]]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; y_plus_radius &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [center_x, center_y &lt;span style="color:#f92672"&gt;+&lt;/span&gt; radius, xyz[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;]]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; x_minus_radius &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [center_x &lt;span style="color:#f92672"&gt;-&lt;/span&gt; radius, center_y, xyz[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;]]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; y_minus_radius &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [center_x, center_y &lt;span style="color:#f92672"&gt;-&lt;/span&gt; radius, xyz[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;]]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; extremevalue_order &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [x_plus_radius, y_plus_radius, x_minus_radius, y_minus_radius]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (arc[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;-&lt;/span&gt; arc[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]) &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# crossing the 0° line (x-axis), handle overflow with nested if below&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; arc[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;360&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; crossing_angle &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;721&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;90&lt;/span&gt;):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; crossing_angle &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(int(arc[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]), int(arc[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; int(crossing_angle &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;90&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; i &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i &lt;span style="color:#f92672"&gt;-=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(extremevalue_order[i])&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sind Kreismittelpunkt sowie Kreisradius bekannt, kann aus den Anfangs- und Endpunkten der überstrichene Winkel bestimmt werden. Hierdurch wird nun klar, ob Kreisanfangspunkt und/oder -Endpunkt selbst Extremwerte darstellen, oder ob durch Überstreichen eines Koordinatennullpunktes zusätzliche Extremwerte entstehen. Genau dies tut das obige Codebeispiel.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-09-08-arcext.avif" alt="Image: Possible coordinate extreme values of an arc segment" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="datentabelle"&gt;Datentabelle&lt;/h3&gt;&#10;&lt;p&gt;Alle möglichen Extremwerte (zwei bei einer Linie, bis zu vier bei einem Kreissegment) werden nun in einer Datentabelle gesammelt. Sobald jede G-Codezeile ausgewertet und die Liste damit komplett ist, werden die Maximal- und Minimalwerte für jede Achse aus dieser Liste bestimmt.&lt;/p&gt;&#10;&lt;p&gt;So entstehen schließlich vier &lt;code&gt;XYZ&lt;/code&gt;-Koordinaten, welche für jede Raumrichtung die maximale Ausdehnung des Werkstückes beschreibt.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-09-08-ext.avif" alt="Image: Visual example of an extreme value dataset for a simple geometry" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Parallel durchsucht das Programm alle Befehle an die Z-Achse und merkt sich die maximale Eintauchtiefe.&lt;/p&gt;&#10;&lt;h3 id="ausgabe"&gt;Ausgabe&lt;/h3&gt;&#10;&lt;p&gt;Das Kommandozeilenprogramm verfügt über keine visuelle Ausgabe. Daher beschränke ich mich auf die Erstellung eines Simulationsprogramms für die Fräsmaschine, in dem die Werkstückausdehnung angefahren wird und bei jedem Punkt pausiert:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G90&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Go to coordinate zero&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MSG &lt;span style="color:#e6db74"&gt;&amp;#34;Zmin of this job: -1.0&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 Z40&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 X0 Y0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G01 Z15 F1200&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MSG &lt;span style="color:#e6db74"&gt;&amp;#34;PathPreview: Hit START to go to Ymin: [&amp;#39;0.0&amp;#39;, &amp;#39;-5.66&amp;#39;]&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;M00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 Z40&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 X0.0 Y-5.66&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G01 Z15 F1200&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MSG &lt;span style="color:#e6db74"&gt;&amp;#34;PathPreview: Hit START to go to Xmin: [&amp;#39;-5.66&amp;#39;, &amp;#39;0.0&amp;#39;]&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;M00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 Z40&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 X-5.66 Y0.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G01 Z15 F1200&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MSG &lt;span style="color:#e6db74"&gt;&amp;#34;PathPreview: Hit START to go to Ymax: [&amp;#39;0.0&amp;#39;, &amp;#39;5.66&amp;#39;]&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;M00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 Z40&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 X0.0 Y5.66&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G01 Z15 F1200&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MSG &lt;span style="color:#e6db74"&gt;&amp;#34;PathPreview: Hit START to go to Xmax: [&amp;#39;5.66&amp;#39;, &amp;#39;0.0&amp;#39;]&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;M00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 Z40&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G00 X5.66 Y0.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;G01 Z15 F1200&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Obiges Beispiel fährt die Extremwerte eines Kreises mit &lt;code&gt;r=5.66mm&lt;/code&gt; ab. Dabei fährt es stets eine Sicherheitshöhe von &lt;code&gt;Z40&lt;/code&gt; zum Überstreichen der Punkte an und bewegt die Z-Achse bei Ankunft langsam mit &lt;code&gt;F1200&lt;/code&gt; auf die Zielhöhe von &lt;code&gt;Z15&lt;/code&gt;. Wichtig dabei ist, dass der Nullpunkt in &lt;code&gt;Z&lt;/code&gt; vorher korrekt vermessen wurde - sonst kommt es möglicherweise zur Kollision mit dem Werkstück.&lt;/p&gt;&#10;&lt;h2 id="test-und-verifikation"&gt;Test und Verifikation&lt;/h2&gt;&#10;&lt;p&gt;Um zu sehen ob das Programm auch wie erwartet funktioniert habe ich drei Testdateien erstellt:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Unit tests: Prüft einzelne Funktionen im Programm, z.B. ob Linien- und Kreissegmente korrekt erkannt werden&lt;/li&gt;&#10;&lt;li&gt;integration tests: Testen, ob Funktionen im Verbund korrekt funktionieren, z.B. die richtige Berechnung von Extremwerten anhand von Eingabewerten&lt;/li&gt;&#10;&lt;li&gt;End to end tests: Diese Tests prüfen über die gesamte Funktionskette, z.B. ob falsch formatierte Eingabedateien erkannt werden&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-09-08-unittest.avif" alt="Image: Test result summary for passtdas" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Zum Schluss erstelle ich mehrere Testdateien mit dem Programm und fahre die daraus erzeugten Extremwerte mit meiner CNC an.&lt;/p&gt;&#10;&lt;h2 id="open-source-software"&gt;Open-source software&lt;/h2&gt;&#10;&lt;p&gt;Der Quellcode für &lt;em&gt;passtdas&lt;/em&gt; steht kostenlos auf &lt;a href="https://github.com/Schallbert/passtdas" target="_blank" rel="noopener noreferrer" class="external-link"&gt;GitHub&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zur Verfügung, muss allerdings durch Python interpretiert werden.&lt;/p&gt;&#10;&lt;p&gt;Ich baue ein Endanwenderfreundliches und leicht zu bedienendes Programm daraus, sobald genügend Leute den Wunsch dafür geäußert haben.&lt;/p&gt;&#10;</description></item><item><title>Giteas *act_runner* rootless ausführen</title><link>https://blog.schallbert.de/gitea-act-runner-dind/</link><pubDate>Sun, 10 Aug 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/gitea-act-runner-dind/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-08-10-gitea-act-runner-dind-rootless-thumb.avif"&#10; class="post-cover"&#10; alt="Image: Symbolized Docker containers next to text &amp;#39;&amp;#39;rootless act_runner DinD&amp;#39;&amp;#39;"&#10; title="Giteas *act_runner* rootless ausführen" /&gt;&#10;&lt;p&gt;In diesem Artikel zeige ich, wie ich den &lt;em&gt;act_runner&lt;/em&gt; meiner &lt;em&gt;Gitea&lt;/em&gt;-Instanz von &lt;code&gt;gitea/act_runner&lt;/code&gt; auf &lt;code&gt;gitea/act_runner:latest-dind-rootless&lt;/code&gt; umstelle.&lt;/p&gt;&#10;&lt;p&gt;Warum dieser Aufwand? Weil &lt;code&gt;act_runner&lt;/code&gt; in &lt;em&gt;Docker&lt;/em&gt; läuft und Zugriff auf den Daemon per &lt;code&gt;/var/run/docker.sock&lt;/code&gt; als sogenanntes Volume (gemeint im Sinne von Datenträger) bzw. Bind-Mount benötigt, um zu funktionieren. Der Besitzer dieses Sockets ist &lt;code&gt;root&lt;/code&gt;, wodurch der Container praktisch Vollzugriff auf das Hostsystem erhält.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;RULE #1 - Do not expose the Docker daemon socket (even to the containers)&amp;rdquo; - OWASP / Docker Security Cheat Sheet&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Dies ist hoch riskant, denn es liegt im Prinzip des Runners, von Dritten erzeugten Job-Code auszuführen. Ein offenes Einfallstor. In Verbindung mit direktem Durchgriff auf das Hostsystem kann das im Falle eines erfolgreichen Angriffes einen Totalausfall bzw. die &amp;ldquo;feindliche Übernahme&amp;rdquo; meiner Infrastruktur bedeuten.&lt;/p&gt;&#10;&lt;h2 id="gefahr-durch-dockersock"&gt;Gefahr durch &lt;code&gt;docker.sock&lt;/code&gt;&lt;/h2&gt;&#10;&lt;p&gt;Ich richte mich in dieser Anleitung nach der Empfehlung von &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#docker-security-cheat-sheet" target="_blank" rel="noopener noreferrer" class="external-link"&gt;OWASP zum Thema Docker-Sicherheit&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und wende deren Regel(n) in der Praxis an. OWASP steht für &amp;ldquo;Open Web Application Security Project&amp;rdquo;. Es ist eine Organisation, die sich für höhere Sicherheit von Webanwendungen einsetzt und dabei Anwender wie mich mit kostenfreien Artikeln, Dokumentation und Technologie unterstützt.&lt;/p&gt;&#10;&lt;p&gt;Die unter &lt;strong&gt;Regel 1&lt;/strong&gt; beschriebenen Maßnahmen lauten:&lt;/p&gt;&#10;&lt;h3 id="dockers-tcp-socket-abgeschaltet-lassen"&gt;Dockers &lt;em&gt;tcp&lt;/em&gt; Socket abgeschaltet lassen&lt;/h3&gt;&#10;&lt;p&gt;Sollte der Zugang zum &lt;em&gt;Docker Daemon&lt;/em&gt; per &lt;em&gt;tcp&lt;/em&gt; eingeschaltet sein, kann sich mit diesem - sofern keine weiteren Vorkehrungen getroffen wurden - über eine ungesicherte Verbindung und ohne Authentisierung verbunden werden. Der Daemon ist dann durch praktisch jeden Internetnutzer zugänglich und damit angreifbar.&lt;/p&gt;&#10;&lt;p&gt;Wie stelle ich also sicher, dass der &lt;em&gt;tcp&lt;/em&gt; Socket abgeschaltet ist?&#10;Die Anleitung hierzu findet sich in den &lt;a href="https://docs.docker.com/engine/daemon/remote-access/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Docker Docs&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und muss quasi verkehrt herum angewandt werden. Auch dort wird ausdrücklich vor ungeschützter Verfügbarmachung des &lt;em&gt;tcp&lt;/em&gt;-Sockets gewarnt.&lt;/p&gt;&#10;&lt;p&gt;Nach Ausführen aller erforderlichen Schritte lasse ich &lt;a href="https://en.wikipedia.org/wiki/Netstat" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;code&gt;netstat&lt;/code&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; einmal durchlaufen. Es listet mir offene Sockets, Netzwerkschnittstellen und Routing-Tabellen auf:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# check if Docker Daemon&amp;#39;s &amp;#34;dockerd&amp;#34; tcp socket is exposed&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# netstat -lntp | grep dockerd&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# test is a pass if this command does not return anything.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bei mir findet sich kein Eintrag mit &lt;code&gt;dockerd&lt;/code&gt;. Mein Server ist an dieser Stelle schon einmal nicht verwundbar.&lt;/p&gt;&#10;&lt;h3 id="den-docker-socket-varrundockersock-nicht-in-andere-container-einbinden"&gt;Den Docker Socket &lt;code&gt;/var/run/docker.sock&lt;/code&gt; nicht in andere Container einbinden&lt;/h3&gt;&#10;&lt;p&gt;Hier wird es schon etwas schwieriger: &lt;em&gt;act_runner&lt;/em&gt; benötigt den Socket, um Job-Container zu erzeugen, zu verwalten und schließlich wieder zu entsorgen. Ohne Zugriff auf den &lt;em&gt;Docker Daemon&lt;/em&gt; über den Socket funktioniert die Build-Pipeline schlicht und einfach nicht - es sei denn, man möchte komplett auf &lt;em&gt;Docker&lt;/em&gt; verzichten und sowohl den Runner als auch die Build-Jobs direkt auf der Host-Maschine laufen lassen. Damit gingen aber viele Nachteile einher: Verlust der Kapselung, keine Portabilität, schlechte Skalierbarkeit, geringere Sicherheit&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;Aber auch für die Option mit &lt;em&gt;Docker&lt;/em&gt; gibt es eine Lösung: &lt;a href="https://gitea.com/gitea/act_runner/src/branch/main/examples/docker-compose#running-act_runner-using-docker-in-docker-dind" target="_blank" rel="noopener noreferrer" class="external-link"&gt;act_runner Docker-in-Docker aufsetzen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Mit diesem Setup erhält &lt;em&gt;act_runner&lt;/em&gt; einen eigenen &lt;em&gt;Docker Daemon&lt;/em&gt;, jedoch mit eingeschränkten Rechten und ohne Zugriff auf den des Host-Systems. Dieser übernimmt dann den Lebenszyklus von Job-Containern, sodass sie komplett unabhängig vom Host-System laufen.&lt;/p&gt;&#10;&lt;h2 id="docker-in-docker"&gt;Docker in Docker&lt;/h2&gt;&#10;&lt;p&gt;Folgendes Schaubild verdeutlicht den Unterschied:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-08-10-runner-dind-setup.avif" alt="Image: act_runner with standard configuration versus DinD-rootless. The latter has an isolated Docker Daemon running within the act_runner container." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="act_runner-rootless-starten"&gt;Act_runner &amp;ldquo;rootless&amp;rdquo; starten&lt;/h3&gt;&#10;&lt;p&gt;Also folge ich der Anleitung und kopiere mir eine passende &lt;code&gt;docker-compose.yml&lt;/code&gt; zusammen. Wichtig hier:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Es muss &lt;code&gt;privileged: true&lt;/code&gt; gesetzt werden. Ansonsten kann der Docker Daemon im &lt;em&gt;act_runner&lt;/em&gt;-Container nicht richtig starten, da ihm Kernel-Funktionen fehlen. Somit schmiert der ganze Container wieder und wieder ab, ohne dabei hilfreiche Fehlermeldungen zu erzeugen.&lt;/li&gt;&#10;&lt;li&gt;Die Umgebungsvariable &lt;code&gt;DOCKER_HOST=unix:///var/run/user/1000/docker.sock&lt;/code&gt; muss gesetzt sein. Hier wird der Docker Socket über einen nicht-privilegierten User gesteuert und steht dem Runner für die Verwaltung von Job Containern zur Verfügung.&#10;Der Daemon läuft gekapselt im Container und steht nicht in Verbindung mit der Host-Maschine.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-08-10-gitea-act-runner-dind-rootless.avif" alt="Image: DinD-rootless runner is now working fine" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="volume-verwirrung"&gt;Volume-Verwirrung&lt;/h3&gt;&#10;&lt;p&gt;Prima! Da der Runner nun läuft, lasse ich ihn direkt mal auf einen Job los. Leider schlägt das Bauen bereits nach Sekundenbruchteilen mit dieser Meldung fehl:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;# Runner step: Set up job&#10;failed to start container: Error response from daemon: error while creating mount source path &amp;#39;&amp;lt;volumeSourceFullPath&amp;gt;&amp;#39;: mkdir &amp;lt;volumeSourcePath&amp;gt;: permission denied&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hier habe ich stundenlang recherchieren müssen und war lange der falschen Überzeugung, dass es an fehlenden Berechtigungen bei Ordnern auf der Host-Maschine liegt. Später erst verstand ich wirklich, dass Docker-in-Docker genau so gemeint ist, wie es heißt: Nicht nur werden Container durch Container erzeugt, sondern innerhalb des Containers läuft ein separater Docker Daemon!&lt;/p&gt;&#10;&lt;p&gt;Damit funktioniert nämlich der klassische Weg nicht mehr, Volumes in Job Containern direkt vom Host-System per &lt;code&gt;-v /a/b:/x/y&lt;/code&gt; verfügbar zu machen.&#10;Stattdessen müssen Volumes nun durchgereicht werden. Beispiel:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&lt;em&gt;host&lt;/em&gt; directory &lt;code&gt;/opt/server/www/blog-artifacts&lt;/code&gt; -&amp;gt; &lt;em&gt;act_runner&lt;/em&gt; volume &lt;code&gt;/tmp/blog-artifacts&lt;/code&gt; &amp;ndash;&amp;gt; &lt;em&gt;job&lt;/em&gt; container volume &lt;code&gt;/tmp/blog-artifacts&lt;/code&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Das &lt;code&gt;:z&lt;/code&gt; im Volume vom &lt;em&gt;act_runner&lt;/em&gt; wird jetzt wichtig. Es zeigt &lt;em&gt;Docker&lt;/em&gt; gegenüber an, dass dieses Volume zwischen Containern geteilt wird. Diese Volumes müssen nicht nur in der &lt;code&gt;docker-compose.yml&lt;/code&gt; von &lt;em&gt;act_runner&lt;/em&gt; angegeben werden, sondern auch die Job-Skripte im Ordner &lt;code&gt;.gitea/workflows/&lt;/code&gt; sind jetzt entsprechend anzupassen. Hier wird das &lt;code&gt;:z&lt;/code&gt; auf der &amp;ldquo;rechten Seite&amp;rdquo; hingegen nicht benötigt.&lt;/p&gt;&#10;&lt;h3 id="is-not-a-valid-volume"&gt;&amp;ldquo;is not a valid volume&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Allem Aufwand zum Trotz laufen meine Jobs noch immer nicht durch. Dieses Mal wegen einer Fehlermeldung, die &lt;a href="https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/#action-volumes"&gt;mir bereits bekannt vorkam&lt;/a&gt;:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log*" data-lang="log*"&gt;# Runner step: Set up job&#10;[/tmp/blog-artifacts] is not a valid volume, will be ignored&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Also rein in die &lt;code&gt;config.yml&lt;/code&gt; von &lt;em&gt;act_runner&lt;/em&gt; und die Volume-Namen hinzufügen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /gitea/runner/config.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;valid_volumes&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;/tmp/blog-artifacts&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;/tmp/lectures-artifacts&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In dieser Datei kann ich &lt;code&gt;privileged: false&lt;/code&gt; stehen lassen, denn der Job Container selbst benötigt anders als &lt;em&gt;act_runner&lt;/em&gt; keine Kernel-Features.&lt;/p&gt;&#10;&lt;h2 id="berechtigungen-korrekt-setzen"&gt;Berechtigungen korrekt setzen&lt;/h2&gt;&#10;&lt;p&gt;Nun erhalte ich erneut &lt;code&gt;Permission Denied&lt;/code&gt; Fehlermeldungen beim Ausführen meiner Jobs, wenn auch nicht direkt im ersten Schritt der Aktionen. Da ich die Volume-Pfade jetzt bis ins Kleinste geprüft habe, kann es eigentlich nur noch an den Ordnerberechtigungen auf der Host-Maschine liegen.&lt;/p&gt;&#10;&lt;p&gt;Damit die vom Job-Container erzeugten Artefakte durch die Volumes auf meinem Host abgelegt werden können, muss ich das zu schreibende Verzeichnis und alle Unterordner &lt;code&gt;-R&lt;/code&gt; an den vorher definierten, nicht-privilegierten User &lt;code&gt;ID=1000&lt;/code&gt; übergeben:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# chown -R 1000:1000 /target/path/to/artifact/&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Endlich funkioniert alles reibungslos und ich habe der (unwahrscheinlichen, aber möglichen) Übernahme meines Host-Systems durch bösartige Job-Container einen Riegel vorgeschoben.&lt;/p&gt;&#10;</description></item><item><title>Rate limiter für Gitea</title><link>https://blog.schallbert.de/gitea-rate-limiter/</link><pubDate>Fri, 25 Jul 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/gitea-rate-limiter/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-07-25-gitea-rate-limiter-thumb.avif"&#10; class="post-cover"&#10; alt="Image: Gitea&amp;#39;&amp;#39;s logo, a cup of tea, protected by many arrows symbolizing GET requests"&#10; title="Rate limiter für Gitea" /&gt;&#10;&lt;p&gt;Hier halte ich eine Schritt-für-Schritt Anleitung fest, die das Problem meiner &lt;a href="https://blog.schallbert.de/gitea-out-of-memory/"&gt;&lt;em&gt;Gitea&lt;/em&gt;-Abstürze&lt;/a&gt; lösen soll. Die Erfahrung aus der Erstanwendung eines Rate Limiters &lt;a href="https://blog.schallbert.de/fail2ban-with-caddy/"&gt;für meinen Blog&lt;/a&gt; lasse ich hier einfließen mit dem Ziel, dass die Anwendung robuster läuft und gegen ungerichtete Denial of Service Attacken geschützt wird.&lt;/p&gt;&#10;&lt;h2 id="schritt-1-log-quelle-für-rate-limiter-festlegen"&gt;Schritt 1: Log-Quelle für rate limiter festlegen&lt;/h2&gt;&#10;&lt;p&gt;&lt;em&gt;Gitea&lt;/em&gt; erzeugt natürlich selbst Logs, theoretisch bis herunter auf Zugriffsebene durch externe Clients. Doch schaffte ich es in der Vergangenheit nicht, &lt;a href="https://blog.schallbert.de/server-protection/#was-noch-nicht-funktioniert-gitea--fail2ban"&gt;&lt;em&gt;Giteas&lt;/em&gt; Access-Logs aus Docker auszuleiten&lt;/a&gt;. Daher geben wir jetzt dem als Reverse Proxy angeflanschten &lt;em&gt;Caddyserver&lt;/em&gt; die Anweisung, Logs stellvertretend zu erstellen. In ihnen tauchen dann Zugriffe auf das Webinterface von &lt;em&gt;Gitea&lt;/em&gt; auf.&lt;/p&gt;&#10;&lt;p&gt;In der &lt;code&gt;Caddyfile&lt;/code&gt; sieht das Log-Modul total unspektakulär aus.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /caddy/Caddyfile&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#[...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;log {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; output file &lt;span style="color:#e6db74"&gt;/log/&lt;/span&gt;gitea&lt;span style="color:#f92672"&gt;/&lt;/span&gt;access&lt;span style="color:#f92672"&gt;.&lt;/span&gt;log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="internen-traffic-vom-log-ausschließen"&gt;Internen Traffic vom Log ausschließen&lt;/h3&gt;&#10;&lt;p&gt;Nun schaue ich die Logs mal an und sehe Einträge, die ich gar nicht haben will. So erzeugt &lt;em&gt;act_runner&lt;/em&gt; zum Beispiel alle zwei Sekunden einen &lt;code&gt;Fetch Task POST&lt;/code&gt; nach &lt;em&gt;Gitea&lt;/em&gt; und alle zehn Sekunden einen &lt;code&gt;GET&lt;/code&gt; Request mit demselben Ziel als &lt;a href="https://blog.schallbert.de/fix-gitea-runner/"&gt;Health check&lt;/a&gt;. Diese brauchen für mich im Log gar nicht erst aufzutauchen. Mein erster Gedanke hier war, einfach Anfragen von internen IP-Adressen nicht zu loggen. Da &lt;em&gt;Caddy&lt;/em&gt; allerdings als Reverse Proxy fungiert, laufen ausnahmslos alle IP-Adressen &amp;ldquo;intern&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Also muss ich &lt;em&gt;act_runner&lt;/em&gt; spezifische Logs anders erkennen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /caddy/Caddyfile&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#[...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git&lt;span style="color:#f92672"&gt;.&lt;/span&gt;schallbert&lt;span style="color:#f92672"&gt;.&lt;/span&gt;de {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reverse_proxy &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#e6db74"&gt;http&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;//&lt;/span&gt;&lt;span style="color:#e6db74"&gt;gitea&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Enable logging for fail2ban, don&amp;#39;t log for runner&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; log_skip &lt;span style="color:#e6db74"&gt;/api/&lt;/span&gt;actions&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; log {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; output file &lt;span style="color:#e6db74"&gt;/log/&lt;/span&gt;gitea&lt;span style="color:#f92672"&gt;/&lt;/span&gt;access&lt;span style="color:#f92672"&gt;.&lt;/span&gt;log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Die &lt;a href="https://caddyserver.com/docs/caddyfile/directives/log_skip" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;code&gt;log_skip&lt;/code&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Direktive weist &lt;em&gt;caddy&lt;/em&gt; nun an, keine Logs für Zugriffe auf &lt;code&gt;/api/actions*&lt;/code&gt; mehr anzulegen.&lt;/p&gt;&#10;&lt;h3 id="reverse-proxy-remote-ips-anzeigen"&gt;Reverse-Proxy: Remote IPs anzeigen&lt;/h3&gt;&#10;&lt;p&gt;Dennoch habe ich noch immer ein Problem: Wenn im Log alle externen Anfragen auf einer internen IP-Adresse hereinkommen, wie soll ich &amp;ldquo;böse&amp;rdquo; Anfragen dann wegblocken? Eine Websuche &lt;a href="https://caddy.community/t/how-to-get-a-true-remote-ip-behind-caddy-reverse-proxy/22348/2" target="_blank" rel="noopener noreferrer" class="external-link"&gt;zeigt, dass dies für Reverse Proxy in Docker&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ein üblicher Stolperstein ist. Leider helfen mir viele Lösungen nicht, da sie andere Serversoftware wie &lt;em&gt;nginx&lt;/em&gt; verwenden oder andere Services hinter ihren Proxies laufen haben als &lt;em&gt;Gitea.&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;Doch tatsächlich ist es ganz einfach: Nur eine Zeile muss an der richtigen Stelle in die &lt;code&gt;Caddyfile&lt;/code&gt; eingefügt werden und schon kommen die remote IP-Adressen unverfälscht rein.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /caddy/Caddyfile&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#[...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git&lt;span style="color:#f92672"&gt;.&lt;/span&gt;schallbert&lt;span style="color:#f92672"&gt;.&lt;/span&gt;de {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reverse_proxy &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#e6db74"&gt;http&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;//&lt;/span&gt;&lt;span style="color:#e6db74"&gt;gitea&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; trusted_proxies &lt;span style="color:#ae81ff"&gt;172&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Docker-internal netwock traffic runs with these IPs&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#[...] &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Die Direktive &lt;a href="https://caddyserver.com/docs/caddyfile/options#trusted-proxies" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;code&gt;trusted_proxies&lt;/code&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; teilt &lt;em&gt;Caddy&lt;/em&gt; mit, dass dem von &lt;em&gt;Docker&lt;/em&gt; bereitgestelltem Netzwerk vertraut werden kann. Somit wird die eigentliche Quell-IP angezeigt statt der internen Schnittstelladresse des Containers. Genau das brauche ich, um die Adressen später mit &lt;em&gt;fail2ban&lt;/em&gt; analysieren zu können.&lt;/p&gt;&#10;&lt;h2 id="schritt-2-anwendungsfall-bestimmen"&gt;Schritt 2: Anwendungsfall bestimmen&lt;/h2&gt;&#10;&lt;p&gt;Schauen wir uns an, wie viele HTTP &lt;code&gt;200 ok&lt;/code&gt; Anfragen im Grenzfall zwischen Normalnutzung und &amp;ldquo;abuse&amp;rdquo; kommen. Dazu surfe ich auf &lt;em&gt;Gitea&lt;/em&gt; herum und klicke einen Haufen Dinge an, wie ich das als Mensch sonst nie in der Geschwindigkeit tun würde. Danach mache ich eine Auswertung der Logs.&lt;/p&gt;&#10;&lt;p&gt;Damit haben wir erste Richtwerte für den Rate Limiter.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# rate limiter tests&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;findtime = 10s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;maxretry = 10&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;bantime = 6h&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="schritt-3-fail2ban-konfigurieren"&gt;Schritt 3: Fail2ban konfigurieren&lt;/h2&gt;&#10;&lt;p&gt;Anschließend konfigurieren wir Filter und Jail-Datei von &lt;code&gt;fail2ban&lt;/code&gt; so, dass sie die oben definierten Logs analysieren.&lt;/p&gt;&#10;&lt;h3 id="filter"&gt;Filter&lt;/h3&gt;&#10;&lt;p&gt;Ich verwende hier schlicht denselben Filter für meinen Rate-Limiter aus dem &lt;a href="https://blog.schallbert.de/fail2ban-with-caddy/#ordner-filterd"&gt;vorherigen Artikel&lt;/a&gt; wieder. Uns interessieren nur erfolgreiche Anfragen, die wir innerhalb eines Zeitfensters zählen.&lt;/p&gt;&#10;&lt;h3 id="jail"&gt;Jail&lt;/h3&gt;&#10;&lt;p&gt;Ich verwende die Werte aus den rate limiter Tests 1:1 hier in der Jail-Datei weiter. Als Filter verweise ich auf &lt;code&gt;caddy-ratelimit&lt;/code&gt; (Link oben).&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /fail2ban/config/fail2ban/jail.local&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#ae81ff"&gt;gitea-ratelimit]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;enabled = true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;chain = DOCKER-USER&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;port = http,https&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;filter = caddy-ratelimit&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;logpath = /var/log/caddy2/gitea/access.log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;findtime = 10s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;maxretry = 10&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;bantime = 6h&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mit einem Neustart von &lt;em&gt;fail2ban&lt;/em&gt; schalte ich den Schutz scharf.&lt;/p&gt;&#10;&lt;h2 id="schritt-4-rate-limiter-testen"&gt;Schritt 4: Rate limiter testen&lt;/h2&gt;&#10;&lt;p&gt;Hier gehe ich genauso vor wie in meinem Artikel &lt;a href="https://blog.schallbert.de/fail2ban-with-caddy/#test-rate-limiter"&gt;fail2ban with caddy&lt;/a&gt; und komme zum selben Ergebnis. Es funktionert!&lt;/p&gt;&#10;&lt;h2 id="-update-oktober-2025"&gt;⚠️ Update Oktober 2025&lt;/h2&gt;&#10;&lt;p&gt;Wird &lt;em&gt;fail2ban&lt;/em&gt; auf Services in Docker-Containern angewendet, kann mit der oben genannten Konfiguration des &lt;em&gt;jails&lt;/em&gt; Gein Fehler auftreten. Die Behebung habe ich im folgenden Artikel dokumentiert: &lt;a href="https://blog.schallbert.de/fail2ban-error-configuration-bad-value/"&gt;&lt;em&gt;fail2ban&lt;/em&gt;-Fehler &amp;ldquo;Bad value substitution&amp;rdquo; for &amp;lsquo;action&amp;rsquo;&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>Rate limiter mit Caddy und fail2ban</title><link>https://blog.schallbert.de/fail2ban-with-caddy/</link><pubDate>Thu, 10 Jul 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/fail2ban-with-caddy/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-07-10-fail2ban-ratelimit-thumb.avif"&#10; class="post-cover"&#10; alt="Image: block drawing of how caddy interacts with fail2ban to rate limit accesses to my blog"&#10; title="Rate limiter mit Caddy und fail2ban" /&gt;&#10;&lt;p&gt;Hier beschreibe ich, wie man das meist zur &lt;a href="https://blog.schallbert.de/server-protection/"&gt;Abwehr unerlaubter Zugriffsversuche&lt;/a&gt; eingesetze Werkzeug &lt;em&gt;fail2ban&lt;/em&gt; auch zur Begrenzung der Anzahl von erfolgreichen Zugriffen innerhalb eines definierten Zeitfensters verwenden kann. Dies nennt man &amp;ldquo;rate limiting&amp;rdquo; und soll denial of service Attacken vereiteln, bei denen &lt;a href="https://blog.schallbert.de/gitea-out-of-memory/"&gt;mein Server überlastet&lt;/a&gt; wird.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-07-10-fail2ban-ratelimit.avif" alt="Image: block drawing of how caddy interacts with fail2ban to rate limit accesses to my blog" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="caddy-logs-einschalten"&gt;Caddy-Logs einschalten&lt;/h2&gt;&#10;&lt;p&gt;Zuerst müssen wir den Logger des Webservers einschalten. Bei Caddy geht das mit wenigen Zeilen Code, die ich meiner &lt;a href="https://blog.schallbert.de/projects/move-blog-to-own-server/#caddy"&gt;bestehenden Konfiguration&lt;/a&gt; hinzufüge:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# caddy2/config/Caddyfile&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;blog.schallbert.de {&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Define webserver&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;root * /www/blog&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;encode gzip&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;file_server&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Enable logging for fail2ban&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;log {&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;output file /log/blog/access.log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ab jetzt werden im angegebenen Pfad Logs angelegt, die ich mit einer &lt;a href="https://blog.schallbert.de/logrotate-mistake/"&gt;&lt;em&gt;logrotate&lt;/em&gt;-Konfiguration&lt;/a&gt; verwalte.&lt;/p&gt;&#10;&lt;h2 id="fail2ban-als-rate-limiter-konfigurieren"&gt;Fail2ban als Rate Limiter konfigurieren&lt;/h2&gt;&#10;&lt;p&gt;Nochmal kurz zur Wiederholung: &lt;em&gt;fail2ban&lt;/em&gt; greift auf die Paketfilter-Regeln der Maschine zu und modifiziert sozusagen ihre Firewall, um dynamische Angriffe abzuwehren. Damit dies funktioniert, müssen &lt;em&gt;fail2ban&lt;/em&gt; Logdateien zur Verfügung gestellt werden, die eine Host-Applikation wie beispielsweise ein Webserver erzeugt und IP-Adressen der Client-Rechner enthalten.&lt;/p&gt;&#10;&lt;h3 id="vorüberlegung-http-200-ok-als-filter"&gt;Vorüberlegung: &amp;ldquo;http 200 OK&amp;rdquo; als Filter?&lt;/h3&gt;&#10;&lt;p&gt;Auch normale, zulässige Zugriffe &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/200" target="_blank" rel="noopener noreferrer" class="external-link"&gt;200 OK&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; müssen für meinen Rate Limiter zählen.&lt;/p&gt;&#10;&lt;p&gt;Um ein Gefühl zu bekommen wie der Logger Zugriffe aufzeichnet, werfe ich einen Blick in die Logs. Ich lasse mir die Anzahl Einträge zurückgeben, die einen Status &lt;code&gt;200 OK&lt;/code&gt; enthalten. Dazu führe ich mit &lt;em&gt;grep&lt;/em&gt; eine Suche durch und lasse mir die Anzahl Treffer zeilenweise per &lt;code&gt;wc -l&lt;/code&gt; ausrechnen.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:/var/log/caddy2/blog# grep -o &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;status&amp;#34;:200&amp;#39;&lt;/span&gt; access.log | wc -l&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;847&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Gut, es hat heute also bereits 847 Zugriffe gegeben, die &lt;code&gt;200 OK&lt;/code&gt; zurückgemeldet bekommen haben. Nun rufe ich selbst mal die Landing page meiner Blogseite auf und lasse mir dann die Artikelübersicht anzeigen.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:/var/log/caddy2/blog# grep -o &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;status&amp;#34;:200&amp;#39;&lt;/span&gt; access.log | wc -l&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;858&lt;/span&gt; // after calling blog.schallbert.de&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rschallbert@machine:/var/log/caddy2/blog# grep -o &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;status&amp;#34;:200&amp;#39;&lt;/span&gt; access.log | wc -l&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;882&lt;/span&gt; // page loaded after clicking &lt;span style="color:#e6db74"&gt;&amp;#34;Posts&amp;#34;&lt;/span&gt; button&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Whow, das waren 35 Einträge für zwei Klicks! Wenn ich mir das Log so ansehe, dann erklären sich die vielen Zeilen mit Assets - sprich Bildern und Logos, die geladen werden.&lt;/p&gt;&#10;&lt;p&gt;Damit ist leider klar, dass das Filterkriterium &lt;code&gt;status:200&lt;/code&gt; für meinen Rate Limiter nicht ohne Weiteres funktionieren kann. Denn die Anzahl Meldungen hängen maßgeblich von dem jeweiligen Artikel ab. Ich müsste also eine Grenze definieren, über der kein normaler Mensch Zugriffe auf mein Blog erzeugt.&lt;/p&gt;&#10;&lt;p&gt;Um nur &amp;ldquo;echte&amp;rdquo; Zugriffe auf meine Seiten zu zählen, muss ich die Assets in den Logs irgendwie ausschließen. Glücklicherweise gibt es dafür eine einfache &lt;a href="https://caddyserver.com/docs/caddyfile/directives/log_skip" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Direktive in Caddy&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# caddy2/config/Caddyfile&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Enable logging for fail2ban&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; log_skip &lt;span style="color:#e6db74"&gt;/assets*&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; log {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; output file /&lt;/span&gt;log&lt;span style="color:#f92672"&gt;/&lt;/span&gt;blog&lt;span style="color:#f92672"&gt;/&lt;/span&gt;access&lt;span style="color:#f92672"&gt;.&lt;/span&gt;log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;log_skip&lt;/code&gt; sorgt nun dafür, dass alle Zugriffe auf Dateien im &lt;code&gt;assets/&lt;/code&gt;-Ordner und darunter nicht mitgeloggt werden.&lt;br&gt;&#10;Das Verhalten von Fail2ban wird mittels zweier Konfigurationsdateien festgelegt:&lt;/p&gt;&#10;&lt;h3 id="ordner-filterd"&gt;Ordner &lt;code&gt;filter.d&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;&lt;code&gt;filter.d/&amp;lt;filter-name&amp;gt;.conf&lt;/code&gt; beinhaltet die Definition eines zu betrachtenden Ereignisses. So kann die &lt;code&gt;failregex&lt;/code&gt; ein &amp;ldquo;Seite nicht gefunden&amp;rdquo; &lt;code&gt;http 404&lt;/code&gt; Statuscode ebenso zum Auslösen des Paketfilters verwendet werden wie der erfolgreiche Zugriff für den Aufbau meines Rate Limiter, welcher auf &lt;code&gt;200 OK&lt;/code&gt; reagiert. Abgeschrieben habe ich für die Regex bei &lt;a href="https://www.kassner.com.br/en/2023/09/10/fail2ban-caddy-json-logs/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Rafael Kassner&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /opt/fail2ban/config/filter.d/caddy-ratelimit.conf&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#ae81ff"&gt;Definition]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;failregex = &amp;#34;client_ip&amp;#34;:&amp;#34;&amp;lt;HOST&amp;gt;&amp;#34;(.*)&amp;#34;status&amp;#34;:200&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;datepattern = \d+&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;ignoreregex =&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Außerdem werden in der Datei weitere Randbedingungen bestimmt, z.B. das Datumsformat &lt;code&gt;datepattern&lt;/code&gt; an den Log-Output des zu schützenden Systems angepasst.&lt;/p&gt;&#10;&lt;h3 id="datei-jaillocal"&gt;Datei &lt;code&gt;jail.local&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;&lt;code&gt;jail.local&lt;/code&gt; bestimmt die Bedingungen, bei deren Eintritt der Paketfilter für die Client-IP aktiv wird und weitere Zugriffsversuche blockt. Für den Einsatz als Rate Limiter benötige ich folgende Felder:&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;findtime&lt;/code&gt; - das Zeitfenster, in welchem Angriffe gezählt werden, &lt;code&gt;maxretry&lt;/code&gt; ist die Anzahl zulässiger Versuche im Zeitfenster und &lt;code&gt;bantime&lt;/code&gt; ist die Zeit, für die geblockt wird. &lt;code&gt;ignoreip&lt;/code&gt; ist meist per Werkseinstellung auf die relevanten internen IP-Adressen vorkonfiguriert. In meinem Falle funkt &lt;em&gt;act_runner&lt;/em&gt; zum Beispiel an intern &lt;em&gt;gitea&lt;/em&gt; zur Abfrage, ob neue Automatisierungsaufgaben anliegen. Diese Zugriffe will ich auf keinen Fall behindern.&lt;/p&gt;&#10;&lt;p&gt;Das im Folgenden verwendete Beispiel für &lt;em&gt;fail2ban&lt;/em&gt; zeigt den Rate Limiter auf meinem Blog. Am Ende sind Dateien und Log-Einträge für &lt;em&gt;gitea&lt;/em&gt; im Wesentlichen gleich. Nur Filternamen und Parameter unterscheiden sich voneinander durch die unterschiedlichen Anforderungen an Webseite und DevOps-Platform.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /opt/fail2ban/jail.local&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#ae81ff"&gt;caddy-ratelimit]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;enabled = true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;chain = DOCKER-USER&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;action = iptables-multiport&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;port = http,https&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;filter = caddy-ratelimit&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;logpath = /var/log/caddy2/blog/access.log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;findtime = 10s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;maxretry = 10&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;bantime = 6h&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sind diese beiden Dateien konfiguriert, kann man &lt;em&gt;fail2ban&lt;/em&gt; neu starten und sich die Logs anschauen. Wenn man noch Fehler in den Dateien hat, gibt &lt;em&gt;fail2ban&lt;/em&gt; folgendes aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ERROR Errors in jail &lt;span style="color:#e6db74"&gt;&amp;#39;caddy-ratelimit&amp;#39;&lt;/span&gt;. Skipping...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tritt dies auf, kann z.B. das Jail nicht dem Filter (muss gleichnamig sein!) nicht zugeordnet werden, eine Feld-Definition wie &lt;code&gt;maxretry&lt;/code&gt; ist falsch getippt oder ein Zahlenformat kann nicht gelesen werden. Wenn alles gut läuft, lautet der Eintrag:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Reading config files: /etc/fail2ban/filter.d/caddy-ratelimit.conf&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="test-rate-limiter"&gt;Rate Limiter testen&lt;/h2&gt;&#10;&lt;p&gt;Nun möchte ich prüfen, ob alles wie erwartet funktioniert.&lt;/p&gt;&#10;&lt;h3 id="filter-regex-überprüfen"&gt;Filter-Regex überprüfen&lt;/h3&gt;&#10;&lt;p&gt;Als ersten Schritt schaue ich auf den Filterbegriff und prüfe, ob er in den Logs zuverlässig gefunden wird. Praktischerweise hat &lt;em&gt;fail2ban&lt;/em&gt; hierfür ein passendes Werkzeug eingebaut: &lt;code&gt;fail2ban-regex &amp;lt;logfile&amp;gt; &amp;lt;filter&amp;gt;&lt;/code&gt;. Ich kann also schlicht die Filterdatei und ein Test-Logfile eingeben und sehe dann, ob ich Treffer bekomme.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;fail2ban-regex ./access.log /etc/fail2ban/filter.d/caddy-ratelimit.conf &#10;&#10;Running tests&#10;=============&#10;&#10;Use filter file : caddy-ratelimit, basedir: /etc/fail2ban&#10;Use datepattern : \d+ : \d+&#10;Use log file : ./access.log.2&#10;Use encoding : UTF-8&#10;&#10;Results&#10;=======&#10;&#10;Failregex: 2009 total&#10;|- #) [# of hits] regular expression&#10;| 1) [2009] &amp;#34;client_ip&amp;#34;:&amp;#34;&amp;lt;HOST&amp;gt;&amp;#34;(.*)&amp;#34;status&amp;#34;:200&#10;`-&#10;&#10;Ignoreregex: 0 total&#10;&#10;Date template hits:&#10;|- [# of hits] date format&#10;| [2685] \d+&#10;`-&#10;&#10;Lines: 2685 lines, 0 ignored, 2009 matched, 676 missed&#10;[processed in 0.12 sec]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Quantitativ bekomme ich, was ich erwarte: das oben beschriebene Suchverfahren per &lt;em&gt;grep&lt;/em&gt; liefert mir exakt dieselben Ergebnisse.&lt;/p&gt;&#10;&lt;h3 id="check-jail"&gt;Jail überprüfen&lt;/h3&gt;&#10;&lt;p&gt;Als nächsten Schritt muss ich testen, ob der Rate Limiter auch greift. Dafür simuliere ich mir Logeinträge. Die Jail-Regeln nehme ich für diesen Test stark zurück, sonst muss ich zu viele Eingaben in kurzer Zeit erzeugen.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /opt/fail2ban/jail.local TEST&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#ae81ff"&gt;...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;findtime = 10s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;maxretry = 2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;bantime = 100s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nach einem Neustart von &lt;em&gt;fail2ban&lt;/em&gt; erzeuge ich mir eine zweite Konsole, wo ich Log-Einträge simulieren kann. Dabei müssen mindestens Zeitpunkt, IP-Adresse des Aufrufenden und Status enthalten sein; ich gehe aber auf Nummer sicher und nehme vollständige Einträge.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;ts&amp;#34;:1751246139,&amp;#34;remote_ip&amp;#34;:&amp;#34;1.1.1.1&amp;#34;,&amp;#34;status&amp;#34;:200,[superLongIrrelevantOtherStuffForFiltering]}&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; /var/log/caddy/blog/access.log&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dies gebe ich mehrfach ein, um über mein Limit von 2 retries zu kommen.&lt;/p&gt;&#10;&lt;p&gt;In der ersten Konsoleninstanz prüfe ich anschließend den &lt;em&gt;fail2ban&lt;/em&gt;-Status für den entsprechenden Filter.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fail2ban-client status caddy-ratelimit&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Status &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; the jail: caddy-ratelimit&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;|- Filter&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| |- Currently failed:&#9;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| |- Total failed:&#9;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;- File list:&#9;/var/log/caddy2/blog/access.log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;- Actions&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |- Currently banned:&#9;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |- Total banned:&#9;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;- Banned IP list:&#9;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Das war nichts. Warum offenbart die fail2ban-Logdatei:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /fail2ban/config/log/fail2ban/fail2ban.log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WARN &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-ratelimit&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Ignoring all log entries older than 20s; &lt;span style="color:#75715e"&gt;# probably messages generated within a fail2ban restart period&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also nochmal mit passenden Zeitstempeln. Diese muss ich aus der aktuellen Systemzeit (&lt;code&gt;$(date +%s.%N)&lt;/code&gt;) ziehen, wenn ich sie ständig anpassen muss. Daher nehme ich eine vollständige Logzeile her, modifiziere sie auf die IP-Adresse &lt;code&gt;1.1.1.1&lt;/code&gt; und füge passende Zeitstempel ein:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;{\&amp;#34;level\&amp;#34;:\&amp;#34;info\&amp;#34;,\&amp;#34;ts\&amp;#34;:$(date +%s.%N),\&amp;#34;logger\&amp;#34;:\&amp;#34;http.log.access.log0\&amp;#34;,\&amp;#34;msg\&amp;#34;:\&amp;#34;handled request\&amp;#34;,\&amp;#34;request\&amp;#34;:{\&amp;#34;remote_ip\&amp;#34;:\&amp;#34;1.1.1.1\&amp;#34;,\&amp;#34;remote_port\&amp;#34;:\&amp;#34;38229\&amp;#34;,\&amp;#34;client_ip\&amp;#34;:\&amp;#34;1.1.1.1\&amp;#34;,\&amp;#34;proto\&amp;#34;:\&amp;#34;HTTP/1.1\&amp;#34;,\&amp;#34;method\&amp;#34;:\&amp;#34;GET\&amp;#34;,\&amp;#34;host\&amp;#34;:\&amp;#34;blog.schallbert.de\&amp;#34;,\&amp;#34;uri\&amp;#34;:\&amp;#34;/vacuum-clamping/\&amp;#34;,\&amp;#34;headers\&amp;#34;:{\&amp;#34;Accept-Encoding\&amp;#34;:[\&amp;#34;gzip, deflate, br\&amp;#34;],\&amp;#34;Connection\&amp;#34;:[\&amp;#34;keep-alive\&amp;#34;],\&amp;#34;User-Agent\&amp;#34;:[\&amp;#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0\&amp;#34;],\&amp;#34;Accept\&amp;#34;:[\&amp;#34;text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\&amp;#34;],\&amp;#34;Accept-Language\&amp;#34;:[\&amp;#34;en-US,en;q=0.5\&amp;#34;]},\&amp;#34;tls\&amp;#34;:{\&amp;#34;resumed\&amp;#34;:false,\&amp;#34;version\&amp;#34;:772,\&amp;#34;cipher_suite\&amp;#34;:4865,\&amp;#34;proto\&amp;#34;:\&amp;#34;http/1.1\&amp;#34;,\&amp;#34;server_name\&amp;#34;:\&amp;#34;blog.schallbert.de\&amp;#34;}},\&amp;#34;bytes_read\&amp;#34;:0,\&amp;#34;user_id\&amp;#34;:\&amp;#34;\&amp;#34;,\&amp;#34;duration\&amp;#34;:0.001207354,\&amp;#34;size\&amp;#34;:12290,\&amp;#34;status\&amp;#34;:200,\&amp;#34;resp_headers\&amp;#34;:{\&amp;#34;Last-Modified\&amp;#34;:[\&amp;#34;Tue, 10 Jun 2025 19:33:01 GMT\&amp;#34;],\&amp;#34;Content-Encoding\&amp;#34;:[\&amp;#34;gzip\&amp;#34;],\&amp;#34;Server\&amp;#34;:[\&amp;#34;Caddy\&amp;#34;],\&amp;#34;Alt-Svc\&amp;#34;:[\&amp;#34;h3=\\\&amp;#34;:443\\\&amp;#34;; ma=2592000\&amp;#34;],\&amp;#34;Vary\&amp;#34;:[\&amp;#34;Accept-Encoding\&amp;#34;],\&amp;#34;Etag\&amp;#34;:[\&amp;#34;\\\&amp;#34;gzip\\\&amp;#34;\&amp;#34;],\&amp;#34;Content-Type\&amp;#34;:[\&amp;#34;text/html; charset=utf-8\&amp;#34;]}}&amp;#34;&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;access.log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nach mehrmaligem Aufruf dieses gebastelten Kommandos erhalte ich nun&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fail2ban-client status caddy-ratelimit&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Status &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; the jail: caddy-ratelimit&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;|- Filter&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| |- Currently failed:&#9;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| |- Total failed:&#9;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;- File list:&#9;/var/log/caddy2/blog/access.log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;- Actions&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |- Currently banned:&#9;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |- Total banned:&#9;&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;- Banned IP list:&#9;1.1.1.1&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Das sieht super aus. Auf zum letzten Schritt.&lt;/p&gt;&#10;&lt;h3 id="paketfilter-überprüfen"&gt;Paketfilter überprüfen&lt;/h3&gt;&#10;&lt;p&gt;Wenn wir nun einmal in die Logs schauen, müsste unser &lt;code&gt;[caddy-ratelimit]&lt;/code&gt; Jail nun bei jeder &amp;ldquo;normalen&amp;rdquo; Anfrage triggern.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine: nano fail2ban/config/log/fail2ban/fail2ban.log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-ratelimit&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP a&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-status&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP b&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-ratelimit&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Ignore 172.18.0.1 by ip&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-status&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Ignore 172.18.0.1 by ip&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-ratelimit&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP c&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-ratelimit&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP c&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-status&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP c&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-status&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP c&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-ratelimit&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP d&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-status&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP d&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-status&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP e&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-ratelimit&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP e&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;timestamp&amp;gt; &amp;lt;id&amp;gt; INFO &lt;span style="color:#f92672"&gt;[&lt;/span&gt;caddy-ratelimit&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Found &amp;lt;IP f&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Um die ganze Kette getestet zu haben müssen wir jetzt noch prüfen, ob die entsprechende IP-Adresse auch wirklich in der Hardware geblockt ist. Dafür gebe ich ein:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# iptables -n -L | grep &lt;span style="color:#e6db74"&gt;&amp;#34;1.1.1.1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;REJECT all -- 1.1.1.1 0.0.0.0/0 reject-with icmp-port-unreachable&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Scheint ja alles in Ordnung zu sein. Für einen letzten Test von einer externen IP-Adresse rufe ich im Browser einen &lt;a href="https://www.deadlinkchecker.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Link Checker&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; auf, der meine Webseite crawlen und damit den Rate Limiter auslösen soll. Obwohl dieser 44 Links in kürzester Zeit durchläuft, wird der Test als &amp;ldquo;bestanden&amp;rdquo; angezeigt. Sehr merkwürdig, &lt;code&gt;[caddy-ratelimit]&lt;/code&gt; hätte anschlagen müssen!&lt;/p&gt;&#10;&lt;h3 id="iptable-chain-input-statt-forward"&gt;iptable-chain INPUT statt FORWARD&lt;/h3&gt;&#10;&lt;p&gt;Verwirrt schaue ich mir mal die ganze &lt;code&gt;iptable&lt;/code&gt; an. Parallel suche ich mir die IP-Adresse des Link Checker heraus. Sie taucht in den &lt;code&gt;iptables&lt;/code&gt; auf. Und trotzdem wird offensichtlich nicht geblockt. Woran das liegt? Ein genauer Blick zeigt:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;Chain INPUT (policy ACCEPT)&#10;target prot opt source destination &#10;f2b-caddy-ratelimit tcp -- 0.0.0.0/0 0.0.0.0/0 multiport dports 22&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Problem hier ist, dass mein Jail auf der &lt;code&gt;INPUT&lt;/code&gt;-Chain liegt. Die Anfragen gehen aber doch nicht direkt an meine Server-Hardware, sondern werden über &lt;em&gt;Docker&lt;/em&gt; an &lt;em&gt;Caddyserver&lt;/em&gt; weitergeleitet. Um zu funktionieren, muss ich auf der &lt;code&gt;FORWARD&lt;/code&gt;-Chain landen, wo &lt;code&gt;DOCKER-USER&lt;/code&gt; bereits ist. Sehr merkwürdig, hatte ich doch in der &lt;code&gt;jail.local&lt;/code&gt; extra &lt;code&gt;chain = DOCKER-USER&lt;/code&gt; angegeben. Irgend etwas muss diese Definition überschreiben.&lt;/p&gt;&#10;&lt;p&gt;Ein unscheinbarer &lt;a href="https://gist.github.com/Rankarusu/23a04ed587b05c6f2b701f2457a127b0?permalink_comment_id=5347631#gistcomment-5347631" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Forenpost&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zusammen mit dem Kommentar &lt;code&gt;multiport&lt;/code&gt; im iptables-Print bringt mich auf die Lösung: Die &lt;code&gt;action = iptables-multiport&lt;/code&gt; Direktive überschreibt mein &lt;code&gt;chain = DOCKER-USER&lt;/code&gt; Statement, denn in der zugehörigen Konfigurationsdatei &lt;code&gt;iptables.conf&lt;/code&gt; wird folgendes gesetzt: &lt;code&gt;chain = INPUT&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Also lösche ich den &lt;code&gt;action&lt;/code&gt; Eintrag einfach, sodass &lt;em&gt;fail2ban&lt;/em&gt; auf den Default für &lt;code&gt;DOCKER-USER&lt;/code&gt; zurückfällt: &lt;code&gt;multiport dports 80,443&lt;/code&gt;.&#10;Nun taucht der Jail-Name auch korrekt in &lt;code&gt;iptables&lt;/code&gt; auf.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert@machine:~# iptables -n -L&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Chain DOCKER-USER &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; references&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;target prot opt source destination &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;f2b-caddy-ratelimit tcp -- 0.0.0.0/0 0.0.0.0/0 multiport dports 80,443&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="erfolg-genießen"&gt;Erfolg genießen&lt;/h2&gt;&#10;&lt;p&gt;Endlich zeigt ein weiterer Link Check, dass mein Rate Limiter funktioniert.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-07-10-deadlinkcheck-ratelimit-working.avif" alt="Image: deadlinkchecker view for blog.schallbert.de hits configured rate limit and gets blocked subsequentially. Thus it returns a Timeout" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="-update-oktober-2025"&gt;⚠️ Update Oktober 2025&lt;/h2&gt;&#10;&lt;p&gt;In Verbindung mit diesem Artikel gibt es ein wichtiges Update, welches den &lt;a href="https://blog.schallbert.de/fail2ban-error-configuration-bad-value/"&gt;&lt;em&gt;fail2ban&lt;/em&gt; Fehler &amp;ldquo;Bad value substitution&amp;rdquo; for &amp;lsquo;action&amp;rsquo;&lt;/a&gt; behebt. Dieses Problem kann auftauchen, wenn &lt;em&gt;fail2ban&lt;/em&gt; Services prüfen soll, die in Docker-Containern laufen.&lt;/p&gt;&#10;</description></item><item><title>Gitea stürzt ab: Zu viele Anfragen</title><link>https://blog.schallbert.de/gitea-out-of-memory/</link><pubDate>Mon, 30 Jun 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/gitea-out-of-memory/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-06-30-server-load-thumb.avif"&#10; class="post-cover"&#10; alt="Image: server at max load when gitea is flooded with GET requests"&#10; title="Gitea stürzt ab: Zu viele Anfragen" /&gt;&#10;&lt;p&gt;Hier möchte ich kurz darstellen, wie ein Absturz eines Dienstes meinen Server über Stunden wiederholt lahmgelegt hat. Und dies so gründlich, dass ich nur aus der Konsole des Anbieters überhaupt einen Neustart durchführen konnte. Ich erkläre, wie es dazu kam und wie ich dieses und ähnliche Probleme in Zukunft vermeiden möchte.&lt;/p&gt;&#10;&lt;h2 id="ich-wurde-angegriffen-oder"&gt;Ich wurde angegriffen. Oder?&lt;/h2&gt;&#10;&lt;p&gt;Ich saß gerade an einem Artikel, den ich später posten wollte. Zur Sicherheit erstellte ich wie üblich einen Commit und wollte ihn auf meine &lt;em&gt;Gitea&lt;/em&gt;-Instanz schieben. Doch mein &lt;code&gt;git push&lt;/code&gt; Befehl lief einfach nicht durch.&lt;/p&gt;&#10;&lt;p&gt;Verwundert versuchte ich, meine Webseite aufzurufen. Zurück kam:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-06-30-404-message.avif" alt="Image: A browser&amp;#39;s timeout error message `Could not complete your request`" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Merkwürdig. Anschließend wollte ich mich auf meinem Server einloggen, um nach dem Rechten zu sehen: &lt;code&gt;ssh &amp;lt;servername&amp;gt;&lt;/code&gt;. Wieder blieb das Terminal ohne Reaktion. Mist!&lt;/p&gt;&#10;&lt;p&gt;Timeout. Als letzte Rettung meldete ich mich bei meinem Hoster an und schaute auf die Graphen des Servers:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-06-30-server-load.avif" alt="Image: 200% CPU load on my server for nearly two hours" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Oh, was ist denn da los? Nun versuche ich über die Weboberfläche, den Server herunterzufahren. Klappt auch nicht. Erst ein harter Neustart ist erfolgreich.&#10;Ich kann mich wieder per SSH einloggen und sehe, dass auch alle &lt;em&gt;Docker&lt;/em&gt;-Container wieder ganz normal hochfahren.&lt;/p&gt;&#10;&lt;h2 id="was-war-passiert"&gt;Was war passiert?&lt;/h2&gt;&#10;&lt;p&gt;Gut, dass ich eine Woche lang alle Logs aufbewahre. So und über die Auslastung der Maschine über Zeit kann ich einigermaßen rekonstruieren, was passiert ist.&lt;/p&gt;&#10;&lt;h3 id="welche-logs-helfen"&gt;Welche Logs helfen?&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Crash lokalisieren! Auslösende Anwendung finden im &lt;code&gt;kern.log&lt;/code&gt;, Zeitstempel notieren.&lt;/li&gt;&#10;&lt;li&gt;Gibt es systemweite Auswirkungen oder andere Dienste, die beeinträchtigt werden? Im &lt;code&gt;syslog&lt;/code&gt; nachsehen.&lt;/li&gt;&#10;&lt;li&gt;Falls der Verdacht aufkommt, das System sei möglicherweise gehackt worden: &lt;code&gt;auth.log&lt;/code&gt; hat die Details.&lt;/li&gt;&#10;&lt;li&gt;Sollte die betroffene Anwendung im Container laufen, helfen entsprechende Logs dort möglicherweise weiter.&lt;/li&gt;&#10;&lt;li&gt;Logs der Anwendung selbst sichten. Besonders die Zeit kurz vor dem Crash ist interessant.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Und hier nochmal im Detail die Logs, die ich mir für meine Verhalten angesehen habe und wo sie zu finden sind.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;log name&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;purpose&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;relevant content for this issue&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;/var/log/syslog&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;System-wide (bare metal) messages&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(docker) warning: &amp;ldquo;health check for container &lt;code&gt;&amp;lt;ID&amp;gt;&lt;/code&gt; timeout&amp;rdquo; (containerd) error: &amp;ldquo;ttrpc: received message on inactive stream&amp;rdquo;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;/var/log/auth.log&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Contains authentication messages from external hosts, in my case mostly SSH&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;None&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;/var/log/kern.log&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Logs all app, service, daemon and system crashes&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Out of memory: Killed process &lt;code&gt;&amp;lt;ID&amp;gt;&lt;/code&gt; (&lt;code&gt;&amp;lt;serviceName&amp;gt;&lt;/code&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;[.]/gitea/log/gitea.log&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Protocol for requests, actions on Gitea&amp;rsquo;s web interface, repo changes etc.&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;GET&lt;/code&gt; requests, crash/restart indications&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;docker container logs &lt;code&gt;&amp;lt;containerID&amp;gt;&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Docker&amp;rsquo;s logs for the container in question&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Received signal 15; terminating. (SIGTERM)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="kernlogsyslog"&gt;kern.log/syslog&lt;/h3&gt;&#10;&lt;p&gt;Hier sehe ich neben dem Stack trace, was genau passiert ist. Die am besten verständlichste Meldung ist folgende:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;Out of memory: Killed process `&amp;lt;ID&amp;gt;` (gitea)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Gitea ist also der Stecker gezogen worden, weil es praktisch alle Systemressourcen gezogen hatte.&#10;Wenn ich im Log nach oben scrolle, sehe ich ein paar Minuten vorher:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;timestamp&amp;gt; &amp;lt;machineName&amp;gt; dockerd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;716&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: level&lt;span style="color:#f92672"&gt;=&lt;/span&gt;warning msg&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Health check for container &amp;lt;ID&amp;gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;timestamp&amp;gt; &amp;lt;machineName&amp;gt; dockerd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;716&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: level&lt;span style="color:#f92672"&gt;=&lt;/span&gt;error msg&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;stream copy error: reading from a closed fifo&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bereits hier sehe ich Warnungen, dass der gitea-Container nicht sauber arbeitet. Ein weiterer Vorteil, den das Einbauen von &amp;ldquo;Healthchecks&amp;rdquo; in der &lt;code&gt;docker-compose.yml&lt;/code&gt; Datei hat. Über Zweck und Einbau von Healthchecks in &lt;em&gt;docker&lt;/em&gt; hatte ich aus anderen Gründen bereits &lt;a href="https://blog.schallbert.de/fix-gitea-runner/#healthcheck"&gt;einen Artikel verfasst&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 id="gitealog"&gt;gitea.log&lt;/h3&gt;&#10;&lt;p&gt;Folgender Log-Eintrag zeigt wegen eines &lt;a href="https://github.com/go-gitea/gitea/issues/22275" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Bugs in einem Submodul&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; indirekt an, dass &lt;em&gt;Gitea&lt;/em&gt; gerade neu gestartet ist:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cmd/web.go:205:serveInstalled&lt;span style="color:#f92672"&gt;()&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;W&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Table system_setting Column version db default is , struct default is &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Und weiter oben im Log nun:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-06-30-attack-log.avif" alt="Image: Gitea log showing numerous GET request entries, many of which for a specific large file (MiB range), following a typical Gitea startup message" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Sehr interessant. Die &lt;code&gt;highlight.css&lt;/code&gt; liegt in meinem öffentlichen Repo. Auf ihrer Grundlage erhält die Seite &lt;a href="https://lectures.schallbert.de/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;lectures.schallbert.de&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ihr Aussehen. Diese Datei ist ziemlich groß, fast &lt;code&gt;1MiB&lt;/code&gt;. Und sie wird hier gleich zig male geladen, und das praktisch für jeden Commit.&lt;/p&gt;&#10;&lt;p&gt;Nun schaue ich mir weitere Crashes in der Vergangenheit an. Immer sind es bursts von &lt;code&gt;GET&lt;/code&gt;-Befehlen auf große Dateien oder die Anforderung von &lt;code&gt;compares&lt;/code&gt; zwischen zwei Branches des Repository, die einem Aufhängen meines Servers vorausgehen.&lt;/p&gt;&#10;&lt;h2 id="wer-steckt-dahinter"&gt;Wer steckt dahinter?&lt;/h2&gt;&#10;&lt;p&gt;Alle crash-auslösenden Requests kommen aus dem selben IP-Adressraum. Die Abstürze starteten bereits vor ein paar Wochen. Zumeist aber zu Tageszeiten, die ich (und anscheinend auch viele meiner LeserInnen) nicht mitbekamen. Und nach ein paar Minuten war der Server stets wieder im Normalbetrieb.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-06-30-gcp-dos.avif" alt="Image: Whois-request for the IP that had my server crashed, owner: Google LLC (GCP)" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Oha, die Spur führt zu Google&amp;rsquo;s Cloud Platform (GCP).&lt;/p&gt;&#10;&lt;h3 id="aber-ich-hatte-doch-robots-ausgesperrt"&gt;Aber ich hatte doch Robots ausgesperrt?&lt;/h3&gt;&#10;&lt;p&gt;In der Tat hatte ich die &lt;a href="https://blog.schallbert.de/gitea-search-indexation/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Suchmaschinenindexierung für Gitea gestoppt&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Merkwürdig. Oder habe ich es hier gar nicht mit einer der Google-spiders oder &amp;ldquo;KI&amp;rdquo;-Scraper, sondern mit einem fiesen Hacker zu tun, der sich bei &amp;ldquo;Google Cloud&amp;rdquo; eine virtuelle Maschine gemietet hat?&lt;/p&gt;&#10;&lt;h3 id="log-recherche-wie-häufig-wird-die-datei-angefragt"&gt;Log-Recherche: Wie häufig wird die Datei angefragt?&lt;/h3&gt;&#10;&lt;p&gt;Ein Spider würde ja höchstens einmal alle paar Wochen alle meine Seiten durchsuchen, richtig? Und sich hoffentlich nicht über meine &lt;code&gt;robots.txt&lt;/code&gt; hinwegsetzen. Erst recht nicht würde ein Crawler mehrfach und in kurzen Abständen denselben Request stellen, oder?&lt;/p&gt;&#10;&lt;p&gt;Zum dies zu prüfen, suche ich in den *Gitea Logs nach Einträgen von GET-Anfragen auf einer der großen und damit ressourcenintensiv zu übertragenen Dateien:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gunzip gitea.log.&amp;lt;date.rotateID&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;grep &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;filename&amp;gt;&amp;#34;&lt;/span&gt; gitea.log.&amp;lt;date.rotateID&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In der Liste sehe ich, dass dieselbe Anfrage auf dieselbe Zieldatei von derselben IP-Adresse mehrfach und binnen Sekunden gestellt wird. Kurz vor dem Crash benötigt mein Server schon fast &lt;code&gt;4sec&lt;/code&gt;, um die Anfrage zu bedienen.&lt;/p&gt;&#10;&lt;p&gt;Auch sehe ich, dass sich die IP-Adresse alle paar Stunden ändert.&lt;/p&gt;&#10;&lt;h2 id="einen-dos-angriff-parieren"&gt;Einen DoS-Angriff parieren&lt;/h2&gt;&#10;&lt;p&gt;Zusammenfassend muss ich zum Schluss kommen, dass ich aus dem Adressraum des GCP per &lt;a href="https://en.wikipedia.org/wiki/Denial-of-service_attack" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Denial-of-service&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; angegriffen werde.&lt;/p&gt;&#10;&lt;p&gt;Um ein bisschen mehr Hintergrundwissen zu sammeln, besuche ich ein paar Websites zum Thema. Dort lerne ich, dass &lt;em&gt;gitea&lt;/em&gt; auf meinem Server unter einer &lt;a href="https://www.geeksforgeeks.org/computer-networks/types-of-dos-attacks/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Application Layer Attack&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zusammenbricht. Jetzt da ich weiß, was passiert und wie das Problem heißt, fällt das Finden von Lösungen gleich viel leichter 😅&lt;/p&gt;&#10;&lt;p&gt;Ich will mich natürlich nicht kampflos ergeben, indem ich meine Gitea-Instanz dauerhaft vom Netz nehme. Also, welche Optionen habe ich?&lt;/p&gt;&#10;&lt;h3 id="mehr-ressourcen-bereitstellen"&gt;Mehr Ressourcen bereitstellen&lt;/h3&gt;&#10;&lt;p&gt;Zugegeben, meine Maschine hat nur &lt;code&gt;40GB&lt;/code&gt; Speicher und &lt;code&gt;2GB VRAM&lt;/code&gt; sowie eine mickrige 2-Kern CPU aus 2009. Ich könnte einen leistungsfähigeren Server buchen, um Lastspitzen besser abreiten zu können. Doch dies würde den Angriff nicht verhindern, sondern nur seine Auswirkungen abmildern.&lt;/p&gt;&#10;&lt;h3 id="rate-limiting-direkt-im-webserver"&gt;Rate limiting direkt im Webserver&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Rate_limiting" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Rate limiter&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; begrenzen die Anzahl Anfragen eines Clients innerhalb eines Zeitfensters. Dadurch werden die Ressourcen des Servers geschont. Dies ist eine &amp;ldquo;weiche&amp;rdquo; Abwehr von Dos-Attacken, denn auslösende IP-Adressen werden mit einer Fehlermeldung kurzzeitig und sanft abgewehrt. Üblicherweise wird &lt;code&gt;HTTP status code 429&lt;/code&gt; &amp;ldquo;Too Many Requests&amp;rdquo; zurückgegeben, wenn der Limiter eingreift.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Gitea&lt;/em&gt; verfügt über keinen Rate Limiter. In &lt;a href="https://blog.schallbert.de/projects/move-blog-to-own-server/#web-access"&gt;meinem Setup&lt;/a&gt; läuft &lt;em&gt;Gitea&lt;/em&gt; hinter einem reverse proxy, der von meinem &lt;em&gt;Caddyserver&lt;/em&gt; bereitgestellt wird. Hier also müsste ich ansetzen. Für &lt;em&gt;Caddy&lt;/em&gt; gibt es Rate limiter nur als &lt;a href="https://caddyserver.com/docs/modules/http.handlers.rate_limit" target="_blank" rel="noopener noreferrer" class="external-link"&gt;externe Module&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, welche manuell in &lt;em&gt;xcaddy&lt;/em&gt; nachinstalliert und eingerichtet werden müssen.&lt;/p&gt;&#10;&lt;h3 id="blocken-mit-ip-table-regeln"&gt;Blocken mit IP-table Regeln&lt;/h3&gt;&#10;&lt;p&gt;Hier könnte man sich erneut &lt;em&gt;fail2ban&lt;/em&gt; hernehmen und mehrfache Anfragen derselben Ressource von einer IP-Adresse schlicht blocken. &lt;em&gt;Gitea&lt;/em&gt; hat in der &lt;a href="https://docs.gitea.com/next/administration/fail2ban-setup" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Dokumentation eine Beschreibung der Einrichtung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. In meinem Falle müsste ich für die Umsetzung dort weitermachen, &lt;a href="https://blog.schallbert.de/server-protection/#was-noch-nicht-funktioniert-gitea--fail2ban"&gt;wo ich bereits einmal aufgegeben hatte&lt;/a&gt; und damit nicht nur SSH, sondern auch normale Seitenaufrufe überwachen.&lt;/p&gt;&#10;&lt;p&gt;Für mich klingt diese Lösung am Sinnvollsten, denn sie trennt klar Funktionen in verschiedene Anwendungen auf. Ich nutze ausschließlich bei mir bereits verfügbare Applikationen: &lt;em&gt;Caddy&lt;/em&gt; würde die Zugriffslogs bereitstellen und &lt;em&gt;fail2ban&lt;/em&gt; müsste sie lesen und Filter in der Konfoguration &lt;code&gt;jail.local&lt;/code&gt; so setzen, dass es sich wie ein Rate Limiter verhält.&lt;/p&gt;&#10;&lt;p&gt;Jetzt schaue ich mal, wie ich &lt;em&gt;Caddy&lt;/em&gt; entsprechend mit &lt;em&gt;fail2ban&lt;/em&gt; verbinden kann. Den weiterführenden Artikel &lt;a href="https://blog.schallbert.de/fail2ban-with-caddy/"&gt;Fail2ban mit Caddy einrichten&lt;/a&gt; verlinke ich direkt mal 🙂&lt;/p&gt;&#10;</description></item><item><title>Webshop selber aufsetzen - Teil3</title><link>https://blog.schallbert.de/ecommerce-selection/</link><pubDate>Sun, 15 Jun 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/ecommerce-selection/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-06-15-opencart-thumb.avif"&#10; class="post-cover"&#10; alt="Image: Snip of OpenCart&amp;#39;s article about data safety"&#10; title="Webshop selber aufsetzen - Teil3" /&gt;&#10;&lt;h2 id="die-auswahl-anhand-von-leitfragen"&gt;Die Auswahl anhand von Leitfragen&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Welche Lösung ist einfach gestrickt und mit wenig Zeitaufwand zu installieren?&lt;/li&gt;&#10;&lt;li&gt;Welche Lösung ist leichter zu pflegen?&lt;/li&gt;&#10;&lt;li&gt;Bei welchem System kann ich als Betreiber am einfachsten nachvollziehen, wie was funktioniert (Transparenz, Abhängigkeiten)?&lt;/li&gt;&#10;&lt;li&gt;Wo ist die Angriffsoberfläche geringer?&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="hier-fehlt-noch-einiges"&gt;Hier fehlt noch einiges!&lt;/h2&gt;&#10;&lt;p&gt;In den letzten Wochen ist mir die Lust nach einem eigenen Webshop etwas vergangen, da ich mich erst um die Sicherheit meines Servers und die Einarbeitung alter, fast fertiger Projekte kümmern wollte. Ich werde diese Serie vervollständigen, sobald mich das ich-will-was-Neues-machen-Fieber erneut packt.&lt;/p&gt;&#10;&lt;p&gt;Haltet durch!&#10;&lt;em&gt;Schallbert&lt;/em&gt;&lt;/p&gt;&#10;</description></item><item><title>Webshop selber aufsetzen - Teil2</title><link>https://blog.schallbert.de/ecommerce-alternatives/</link><pubDate>Thu, 15 May 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/ecommerce-alternatives/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-05-15-ecommerce-frameworks-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: logos from different e-commerce solutions"&#10; title="Webshop selber aufsetzen - Teil2" /&gt;&#10;&lt;h2 id="ein-paar-optionen-für-die-webshop-software"&gt;Ein paar Optionen für die Webshop-Software&lt;/h2&gt;&#10;&lt;p&gt;Gehen wir mal davon aus, dass in der Vorauswahl nur Software landet, die meine &lt;a href="https://blog.schallbert.de/ecommerce-requirements/"&gt;Anforderungen&lt;/a&gt; zumindest teilweise erfüllt. Im Folgenden möchte ich ein paar mögliche Ecommerce-Lösungen vorstellen.&lt;/p&gt;&#10;&lt;h3 id="shopify"&gt;Shopify&lt;/h3&gt;&#10;&lt;p&gt;Shopify ist eine alles-inklusive-Paketlösung für den Onlinehandel. Shopify Inc. hat ihren Hauptsitz in Kanada und wickelt das europäische Geschäft über Irland ab. Die auf der Website zu findenden Bereiche &lt;a href="https://www.shopify.com/de/legal" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Rechtliches&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; sowie die Komplexität der Informationsdarstellung zum Thema &lt;a href="https://www.shopify.com/de/legal/impressum?country=de&amp;amp;lang=en" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Datenschutz&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; lassen mich vermuten, dass Shopify ein multinationaler Konzern ist - und nicht nur die Onlineshops selbigen zur Verfügung stellt.&lt;/p&gt;&#10;&lt;p&gt;Aber zurück zum Thema.&lt;/p&gt;&#10;&lt;p&gt;Die Domain sowie das Hosting sind bei Shopify im Preis inbegriffen und der eigene Shop kann ohne Programmierkenntnisse per Weboberfläche, Auswählen eines Themes und Einstellen von ein paar Parametern in kurzer Zeit erstellt werden. Shopify besitzt eine ausführliche Dokumentation und bietet Fortbildungen und allerlei weitere Dienstleistungen für ihre Abonnenten an.&lt;/p&gt;&#10;&lt;p&gt;Doch wo bleibt da der Spaß? Schließlich betreibe ich ein Techblog und bin kein Geschäftsmann im eigentlichen Sinne. Ein bisschen Programmieren muss da schon drin sein. Außerdem gefällt mir als Open-Source-Fan das Konzerngehabe (man schaue sich ihre moderne, mit automatisch abspielenden Videos ausgestattete, auf Marketing-Zwecke durchoptimierte Website an) nicht so besonders gut. Die Konfigurierbarkeit ist bestenfalls mittelmäßig und die Optimierung auf Suchmaschinenergebnisse muss ich wie bei den meisten anderen Lösungen auch selbst erledigen. Was Datenschutz und Sicherheit angeht, betreiben sie die übliche Compliance- und Stand-der-Technik-Kommunikation; ich kann also nicht dahinter schauen und muss davon ausgehen, dass sie ihre Kunden auch im eigenen Interesse gut schützen.&lt;/p&gt;&#10;&lt;p&gt;Shopify bezeichnet sich selbst als &lt;a href="https://www.shopify.com/blog/open-source-ecommerce#5" target="_blank" rel="noopener noreferrer" class="external-link"&gt;closed-source SaaS platform&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Daher streiche ich diese Plattform von meiner Liste präferierter Webshoplösungen, weil ich mich nicht ohne Not von Wohl und Wehe Dritter abhängig machen möchte. Bei Shopify hätte nämlich nicht ich die Kontrolle über Abokosten oder &amp;ldquo;meinen&amp;rdquo; Anteil an der Cloud, sondern Shopify. Nicht ich kann bestimmen, wie viele Transaktionen im &amp;ldquo;Plan&amp;rdquo; inklusive sind, sondern Shopify. Nicht meine Kunden blieben Herren ihrer Daten, sondern Shopify oder ein von ihnen mit der Abwicklung beauftragtes Drittunternehmen.&lt;/p&gt;&#10;&lt;h3 id="wordpress--woocommerce"&gt;Wordpress / WooCommerce&lt;/h3&gt;&#10;&lt;p&gt;Ich kenne Wordpress nur als Moloch. Die Eierlegende Wollmilchsau, die durch eine unübersichtlich gewordene Anzahl an Plugins, Erweiterungen und Funktionen alles kann, was man sich nur wünschen kann. Und vieles mehr, was man nicht braucht.&lt;/p&gt;&#10;&lt;p&gt;Aber es ist quelloffen, kostenfrei und wird laut eigener Darstellung von sehr, sehr vielen Shopbetreibern genutzt. Schauen wir uns also mal die bekannteste Ecommerce-Erweiterung zu Wordpress an: &lt;a href="https://woocommerce.com/document/build-online-store/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;WooCommerce&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Ich fand auf Anhieb zig Anleitungen zum Aufsetzen eines WooCommerce-Shops (Beispielsweise von &lt;a href="https://www.greengeeks.com/blog/set-up-woocommerce-wordpress-ultimate-guide/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;GeeksForGeeks&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; oder &lt;a href="https://themeisle.com/blog/how-to-set-up-woocommerce/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Themeisle&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;), also verzichte ich im Folgenden auf eine eigene Darstellung.&lt;/p&gt;&#10;&lt;p&gt;Nach dem Querlesen einiger Anleitungen fasse ich zusammen:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Ein WooCommerce-Shop lässt sich einfach per Assistent aufsetzen und konfigurieren&lt;/li&gt;&#10;&lt;li&gt;Es stehen sehr viele Themes zur Verfügung und einfach einzubindende Shopdesigns bieten für jeden Geschmack eine passende Optik&lt;/li&gt;&#10;&lt;li&gt;Es hilft, bereits eine Wordpress-Seite zu haben und etwas Basiswissen bezüglich Domains, DNS und Hostern zu besitzen&lt;/li&gt;&#10;&lt;li&gt;Viele Zahlungsdienstleister und einige Versandabwickler sind vorkonfiguriert auswählbar&lt;/li&gt;&#10;&lt;li&gt;Es gibt eine Menge YouTube-Lehrvideos zum Thema und eine ausführliche Dokumentation liegt ebenfalls vor&lt;/li&gt;&#10;&lt;li&gt;Zahllose Plugins erlauben die Optimierung des Shops auch auf sehr spezialisierte Gebiete und Einsatzzwecke&lt;/li&gt;&#10;&lt;li&gt;Allerdings steht dieser Optionsvielfalt gegenüber, dass die Auswahl der &amp;ldquo;besten Lösung&amp;rdquo; schwer fallen kann und dass Optimierungen der Seite hinsichtlich Geschwindigkeit und Auffindbarkeit komplex werden können&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Alles in allem wirkt WooCommerce auf mich wie eine ausgewogene Mischung aus &amp;ldquo;Ich will schnell etwas Brauchbares zusammen klicken&amp;rdquo; und &amp;ldquo;Ich möchte alles konfigurieren und umbauen können&amp;rdquo;.&lt;/p&gt;&#10;&lt;h3 id="snipcart"&gt;Snipcart&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://snipcart.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Snipcart&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; würde ich als &amp;ldquo;Shopping-Plugin&amp;rdquo; für eine beliebige Website verstehen. Auch statische Websites, die eigentlich nur einen Warenkatalog darstellen können, werden mit Snipcart zu einer vollwertigen eCommerce-Lösung. Kernelement ist hier der Warenkorb &amp;ldquo;cart&amp;rdquo;, über den der Kaufvorgang abgewickelt wird.&lt;/p&gt;&#10;&lt;p&gt;Die Schnittstelle ist das &lt;code&gt;HTML Markup&lt;/code&gt; der Webseite. Snipcart wird als &lt;code&gt;JavaScript&lt;/code&gt;-Blob eingebunden und per &lt;code&gt;HTML&lt;/code&gt; referenziert. Gestaltung und Aussehen läuft über cascading style sheets. In meinem Fall also könnte ich das vorhandene &amp;ldquo;Tech Stack&amp;rdquo; bestehend aus einer statischen Jekyll-site mit CD-Pipeline in Gitea auf einer Caddyserver-Instanz auch für den Shop nutzen und mit minimalen Anpassungen eine Optik aus einem Guss für Blog und Shop zaubern. Klingt doch schon einmal prima.&lt;/p&gt;&#10;&lt;p&gt;Snipcart wird von einer kanadischen Firma in Québec entwickelt. Es bezeichnet sich als &amp;ldquo;&lt;a href="https://snipcart.com/ecommerce-inventory-management" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Shopping Cart Platform&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&amp;rdquo;. Für die Installation wird ein Account benötigt und man muss die eigene Shopping-Domain ähnlich wie bei der Google Search Console oder Google Analytics im Web-Frontend von Snipcart angeben, um einen sogenannten &lt;code&gt;API-Key&lt;/code&gt; zu erhalten. Nur mit diesem wird der JavaScript-Blob später auch korrekt funktionieren.&lt;/p&gt;&#10;&lt;p&gt;Und damit kommt das erste größere Problem: Snipcart schränkt meine Freiheit ein.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;link&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rel&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;preconnect&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;https://app.snipcart.com&amp;gt;&amp;#34;&lt;/span&gt; /&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;link&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rel&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;preconnect&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;https://cdn.snipcart.com&amp;gt;&amp;#34;&lt;/span&gt; /&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mir zeigt dies, dass Snipcart die Daten gern &lt;a href="https://snipcart.com/blog/add-shopping-cart-to-website" target="_blank" rel="noopener noreferrer" class="external-link"&gt;bei sich haben will&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Ich muss deren content delivery network für die Abwicklung des Bestellprozesses verwenden und bei mir einbinden. Es ist also mitnichten so, dass alle Verbindungen bei mir bleiben und ich den JS-Code von Snipcart &amp;ldquo;kaufe&amp;rdquo; und im Browser des Kunden bzw. eine dahinter liegende Datenbanklösung auf meinem Server betreibe.&lt;/p&gt;&#10;&lt;p&gt;Wenn einem also volle Autonomie und Kontrolle über die eigene Site nicht super wichtig ist, kann Snipcart eine sehr elegante Shopsystem-Lösung sein. Dies lohnt sich vor allem für bereits bestehende Seiten. Für mich jedoch ist sie damit raus.&lt;/p&gt;&#10;&lt;h3 id="opencart"&gt;OpenCart&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://www.opencart.com/index.php?route=common/home" target="_blank" rel="noopener noreferrer" class="external-link"&gt;OpenCart&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; bietet eine &lt;code&gt;PHP&lt;/code&gt;-basiertes Shopmodul kostenlos und quelloffen an. Es kann sowohl als &amp;ldquo;Software as a Service&amp;rdquo; über Drittanbieter gebucht wie auch per &lt;a href="https://github.com/opencart/opencart/blob/master/INSTALL.md" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Github&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; heruntergeladen und auf eigenen Servern bereitgestellt werden.&lt;/p&gt;&#10;&lt;p&gt;Installation und Administration geschehen über eine Weboberfläche und ist meines Ermessens nach unkompliziert möglich. Auch im DockerHub findet man ein paar Images, mit welchen man direkt loslegen kann.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-05-15-opencart.avif" alt="Image: OpenCart demo, category overview" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;OpenCart ist vollständig an die eigenen Bedürfnisse anpassbar. Auch vorgefertigte Themes können eingebunden werden. Aus Neugier habe ich mal ein paar Webseiten angeschaut, die OpenCart einsetzen: Das Shopsystem ist aus meiner Sicht gut strukturiert und einfach zu bedienen. Jedoch ist die Ladegeschwindigkeit der von mir besuchten Beispielseiten nicht immer super schnell.&lt;/p&gt;&#10;&lt;p&gt;Im Gesamtpaket halte ich OpenCart mit seiner anpassbaren, leichtgewichtigen Shoplösung in &lt;code&gt;PHP&lt;/code&gt; auch durch die immer noch aktive Community (13+ Jahre!) für eine gute Lösung.&lt;/p&gt;&#10;&lt;h3 id="django"&gt;Django&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://www.djangoproject.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Django&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist ein Webframework für &lt;code&gt;Python&lt;/code&gt;. Es bietet viele Funktionen, ermöglicht Erweiterungen durch schlichtes Einbinden von Python-Modulen und man kann auch als Programmier-Einsteiger schnell funktionierende Lösungen erzeugen. Obwohl es eine Menge Bibliotheken beispielsweise für Websites, Shops &lt;a href="https://django-shop.readthedocs.io/en/latest/tutorial/intro.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;wie z.B. django-SHOP&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, Datenbankeinbindungen, Einbindung von Zahlsystemen usw. gibt, muss alles noch selbst zusammen gesteckt und konfiguriert werden.&lt;/p&gt;&#10;&lt;p&gt;Dadurch erhält man zwar volle Kontrolle und eine perfekt an die eigenen Bedürfnisse anpassbare Lösung - doch es gibt auch einen gewichtigen Nachteil: Lernzeit. Die Sicherheit der Datenbank muss selbst hergestellt werden, Validierung von User inputs muss selbst programmiert oder zumindest korrekt konfiguriert werden. Das Abspeichern von Kundenstammdaten passiert standardmäßig erst einmal im Klartext. Zugriffs- und Löschkonzepte müssen selbst erstellt und implementiert werden bzw. man sollte Zeit einplanen, passende Module ausfindig zu machen und zu integrieren.&lt;/p&gt;&#10;&lt;p&gt;Sollen Frameworks verwendet werden, empfiehlt es sich vorab eine aktive Wartung durch die Community zu prüfen. Für mich sieht es beispielsweise so aus, als wäre bei &lt;a href="https://github.com/awesto/django-shop" target="_blank" rel="noopener noreferrer" class="external-link"&gt;django-SHOP&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; bereits seit dem Jahre 2021 keine große Bewegung mehr auf dem Github-Repo. Eine kurze Suche hingegen offenbart &lt;a href="https://github.com/topics/django-ecommerce" target="_blank" rel="noopener noreferrer" class="external-link"&gt;eine Menge Alternativen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; - hier gilt es nun, etwas Passendes ins Auge zu fassen oder gleich selbst das Rad neu zu erfinden 😃&lt;/p&gt;&#10;&lt;h2 id="die-alternative-für-anfänger-wie-mich-ohne-shopsystem"&gt;Die Alternative für Anfänger wie mich: ohne Shopsystem&lt;/h2&gt;&#10;&lt;p&gt;Was ist mit &amp;ldquo;Ohne Shopsystem&amp;rdquo; gemeint? Ein formularbasierter Ansatz. Hier schaut sich der User eine statische Website an, die Katalog- und Produktseiten enthält. Zusätzlich sind ein paar Elemente eingeblendet wie ein Dropdown-Menü für Variante und ein Feld für die Stückzahl. Daneben ein schlichter Button. Wird dieser geklickt, so öffnet sich ein Formular, welches automatisch die geklickten Inhalte überträgt und nach Kontaktdaten des Users fragt.&lt;/p&gt;&#10;&lt;p&gt;Wird alles ordnungsgemäß ausgefüllt, so erzeugt das Formular beim Absenden eine PGP-verschlüsselte Mail an mich. Ich kann dann die Order analog zu manuell erstellten Bestell-Mails sichten, eine Bestellbestätigung senden und die Zahlung anfordern.&lt;/p&gt;&#10;&lt;p&gt;So lässt sich mit wenigen Produkten bei wenigen Kunden ohne großen Aufwand ein Shop-Äquivalent datensparsam umsetzen. Natürlich nicht so bequem wie mit einer Ecommerce-Lösung. Aber für den Anfang reicht das vielleicht trotzdem.&lt;/p&gt;&#10;&lt;h2 id="im-nächsten-artikel"&gt;Im nächsten Artikel&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/ecommerce-selection/"&gt;Auswahl und Implementierung&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>Webshop selber aufsetzen - Teil1</title><link>https://blog.schallbert.de/ecommerce-requirements/</link><pubDate>Tue, 15 Apr 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/ecommerce-requirements/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-04-15-ecommerce-pt1-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: text saying &amp;#39;&amp;#39;E-commerce Pt1: Requirements of a technophiliac&amp;#39;&amp;#39;"&#10; title="Webshop selber aufsetzen - Teil1" /&gt;&#10;&lt;p&gt;Dies ist Teil eins der Artikelserie über den Aufbau meines eigenen Webshops. Ich liste Anforderungen und Wünsche für meine Ecommerce-Lösung auf und grenze mich damit auf ein paar passende Software-Lösungen ein, die ich dann für meinen Webshop verwenden könnte.&lt;/p&gt;&#10;&lt;h2 id="was-mein-webshop-können-muss"&gt;Was mein Webshop können muss&lt;/h2&gt;&#10;&lt;p&gt;Für mich als Betreiber soll der Webshop möglichst schlank, transparent und automatisiert operieren. Ich möchte nur wenig Zeit mit Betrieb, Wartung und Pflege verbringen. Zahlungsabwicklung, Verwaltung des Bestellstatus, Kundenregistrierung, Versenden von Kundeninformation sowie eine große Anzahl Schritte im Versandprozess wie Frankieren und Einpflegen von Versandbestätigungen soll automatisiert stattfinden oder zumindest für die Automatisierung vorbereitet werden.&lt;/p&gt;&#10;&lt;p&gt;Aber auch für Kunden muss der Webshop verständlich aufgebaut sein: Simpel und mit wenigen Klicks von der Startseite bis zum Checkout, leicht zu bedienen, ähnlich zu anderen gängigen Webshops, mit unkomplizierter Zahlungsabwicklung. Dabei sollen so wenige Daten vom Kunden wie möglich erhoben werden.&lt;/p&gt;&#10;&lt;p&gt;Der Webshop muss nur wenige Produkte darstellen. Daher kann die Navigation sehr einfach gehalten werden. Durch die geringe Anzahl Produktkategorien kan möglicherweise komplett auf ein Baumsystem verzichtet werden.&lt;/p&gt;&#10;&lt;p&gt;Schauen wir uns mal schnell im Detail an, welche Funktionen der Webshop am Ende bereitstellen muss.&lt;/p&gt;&#10;&lt;h3 id="funktionen-für-admins"&gt;Funktionen für Admins&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Produkt anlegen&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Titel&lt;/li&gt;&#10;&lt;li&gt;Tags&lt;/li&gt;&#10;&lt;li&gt;Kurzbeschreibung&lt;/li&gt;&#10;&lt;li&gt;Beschreibung&lt;/li&gt;&#10;&lt;li&gt;Bilder&lt;/li&gt;&#10;&lt;li&gt;Videos&lt;/li&gt;&#10;&lt;li&gt;Preis&lt;/li&gt;&#10;&lt;li&gt;Steckbrief / Datenblatt / Anleitung&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ol start="2"&gt;&#10;&lt;li&gt;Produkt editieren&lt;/li&gt;&#10;&lt;li&gt;Produkt entfernen&lt;/li&gt;&#10;&lt;li&gt;Bestellungen verwalten&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Schritt quittieren&lt;/li&gt;&#10;&lt;li&gt;Schritt erneut starten&lt;/li&gt;&#10;&lt;li&gt;Prozess stornieren&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ol start="5"&gt;&#10;&lt;li&gt;Kunden verwalten&#10;&lt;ul&gt;&#10;&lt;li&gt;anlegen (sandbox)&lt;/li&gt;&#10;&lt;li&gt;löschen (Datenschutz)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="funktionen-für-käufer"&gt;Funktionen für Käufer&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Webshop durchstöbern&lt;/li&gt;&#10;&lt;li&gt;Produkt(e) konfigurieren oder Produktvariante auswählen (z.B. Farbe, Qualität)&lt;/li&gt;&#10;&lt;li&gt;Produkt(e) in einen Warenkorb legen&lt;/li&gt;&#10;&lt;li&gt;Warenkorb verwalten&lt;/li&gt;&#10;&lt;li&gt;Registrierung vornehmen (optional, nur zur Kundendatenablage und Bestellverwaltung)&lt;/li&gt;&#10;&lt;li&gt;Versand wählen&lt;/li&gt;&#10;&lt;li&gt;Zahlung vornehmen&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="automatische-funktionen"&gt;Automatische Funktionen&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Zahlungsregistrierung&lt;/li&gt;&#10;&lt;li&gt;Rechnungserstellung und -Versand&lt;/li&gt;&#10;&lt;li&gt;Inventarverwaltung&lt;/li&gt;&#10;&lt;li&gt;Versandbenachrichtigung&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="nichtfunktionale-anforderungen"&gt;Nichtfunktionale Anforderungen&lt;/h2&gt;&#10;&lt;p&gt;Hier liste ich auf, was der Webshop &amp;ldquo;nebenbei&amp;rdquo; noch so alles können soll. Alles, was nicht unmittelbar zur Auftragsabwicklung benötigt oder für Kunden sichtbar wird.&lt;/p&gt;&#10;&lt;h3 id="usability-betreiberseite"&gt;Usability Betreiberseite&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Der Shop soll mit möglichst geringen Abhängigkeiten zu Software Dritter auskommen und nur diejenigen Module einbinden, die unbedingt benötigt werden. Beispiele:&#10;&lt;ul&gt;&#10;&lt;li&gt;Zahlungsabwicklung&lt;/li&gt;&#10;&lt;li&gt;Warenkorb&lt;/li&gt;&#10;&lt;li&gt;Kundenkonto / Adressdaten&lt;/li&gt;&#10;&lt;li&gt;Lagerbestände&lt;/li&gt;&#10;&lt;li&gt;Bestellabwicklung (Mailer) für Käufer&lt;/li&gt;&#10;&lt;li&gt;Rechnungsstellung.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Der Shop soll nur so viele dynamisch erzeugte Elemente enthalten wie unbedingt nötig.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="usability-käuferseite"&gt;Usability Käuferseite&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Der Webshop soll sich wie aktuelle übliche ECommerce-Lösungen anfühlen: Flink, übersichtlich, einfach.&lt;/li&gt;&#10;&lt;li&gt;Kein Zwang zum Kundenkonto.&lt;/li&gt;&#10;&lt;li&gt;Der Shop soll gängige Zahlungsmöglichkeiten unterstützen (Debit, Kredit, PayPal, Apple/GooglePay etc.). Dabei sind nur lokale (in Deutschland ansässige) Zahlungsdienstleister akzeptabel.&lt;/li&gt;&#10;&lt;li&gt;Für den User sollen Lagerbestände bzw. Fertigungszeiten transparent angezeigt werden.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="datenschutz"&gt;Datenschutz&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Der Shop soll nur für die Geschäftsabwicklung notwendige Daten erfassen.&lt;/li&gt;&#10;&lt;li&gt;Falls technisch und rechtlich möglich soll der Shop ohne Cookie-Banner auskommen können.&lt;/li&gt;&#10;&lt;li&gt;Kundenkonten sollen automatisch nach 2 Jahren der Nicht-Nutzung (mit Ankündigung 2 Wochen vorher) gelöscht werden.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="look--feel"&gt;Look &amp;amp; Feel&lt;/h3&gt;&#10;&lt;p&gt;Hier einige Beispiele für Shopdesigns:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://store.caddyserver.com/en-eur/collections/all" target="_blank" rel="noopener noreferrer" class="external-link"&gt;caddyshop&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://dev.to/contentful_blog/how-to-build-an-ecommerce-static-site-with-jekyll-contentful-and-commerce-layer-3c9" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Beispiel-ecommerce mit Jekyll&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.geeksforgeeks.org/e-commerce-website-using-django/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Beispiel-ecommerce mit Django&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Davon abgeleitete Anforderungen:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Der Webshop soll von Aufbau und Farbgebung her an meine Blogseite erinnern und eine einheitliche Optik bieten.&lt;/li&gt;&#10;&lt;li&gt;Es sollen nur statische Fonts ohne Nachladen verwendet werden.&lt;/li&gt;&#10;&lt;li&gt;Produktbeschreibungen sollen als Markdown-Dokumente (oder Ähnlich) hochgeladen werden können.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="datenbank--oder-dateilösung"&gt;Datenbank- oder Dateilösung?&lt;/h2&gt;&#10;&lt;p&gt;Es muss nicht immer gleich eine Datenbank sein. Für viele Anwendungen werden nur Bruchteile der Funktionalität gängiger Datenbanklösungen benötigt, doch erhöht sich die Komplexität des Gesamtsystems erheblich. Sollte z.B. gleichzeitiges Schreiben selten oder nie vorkommen und keine starke Verkettung von Daten vorliegen, so kann eine Dateilösung einfacher, schneller und effizienter sein &lt;a href="https://engineeringkiosk.dev/podcast/episode/129-simplify-your-stack-files-statt-datenbanken/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Link zu einer passenden Engineering Kiosk Podcast-Episode&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Andererseits kommen viele Shopsysteme &amp;ldquo;with batteries included&amp;rdquo;, verfügen also schon von sich aus über eingebaute und für die Programmierer nahezu transparente Datenverwaltung.&lt;/p&gt;&#10;&lt;h3 id="dateilösung-statische-website"&gt;Dateilösung (statische Website)&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Auf Betreiberseite soll sich der Webshop so anfühlen wie ein Git-Projekt (everything is a file).&lt;/li&gt;&#10;&lt;li&gt;Änderungen im Angebot, Artikelbeschreibung, Preise etc. sollen per &lt;code&gt;git push&lt;/code&gt; auf den Server gespielt werden können - wie bei einer statischen Webseite.&lt;/li&gt;&#10;&lt;li&gt;Wird ein Pull Request nach &lt;code&gt;main&lt;/code&gt; gemerged, so soll der Webshop neu bauen (CD-Pipeline) und die Änderungen auf dem Server veröffentlichen.&lt;/li&gt;&#10;&lt;li&gt;Für Inventarverwaltung, Warenkorb, Kundeninteraktion und automatisierte Funktionen sind dynamische Elemente erforderlich. Hier gilt zu entscheiden, was direkt in Javascript und welche Elemente besser per Automatisierungspipeline gelöst werden sollen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="datenbanklösung-dynamische-website"&gt;Datenbanklösung (dynamische Website)&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Produkte werden in der Datenbank gehalten.&lt;/li&gt;&#10;&lt;li&gt;Die Verwaltung des Webshops geschieht per Web-Oberfläche.&lt;/li&gt;&#10;&lt;li&gt;Die Datenbankverwaltung soll teilautomatisiert erfolgen, dabei sind Backups und Verschlüsselung für Kundendaten vorzusehen.&lt;/li&gt;&#10;&lt;li&gt;Die Datenbank ist transparent für den Admin: Alle Einträge hier erfolgen über eine Web-Schnittstelle.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="im-nächsten-artikel"&gt;Im nächsten Artikel&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/ecommerce-alternatives/"&gt;Vergleich verschiedener Ecommerce-Lösungen&lt;/a&gt; für meinen Webshop&lt;/p&gt;&#10;</description></item><item><title>PeerTube im Fediverse</title><link>https://blog.schallbert.de/fediverse-ups-and-downs/</link><pubDate>Tue, 01 Apr 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/fediverse-ups-and-downs/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/announcements/2025-03-17-makertube.jpg"&#10; class="post-cover"&#10; alt="Image: My MakerTube channel &amp;#39;&amp;#39;CNC corner&amp;#39;&amp;#39;"&#10; title="PeerTube im Fediverse" /&gt;&#10;&lt;h2 id="kurze-begriffsklärung"&gt;Kurze Begriffsklärung&lt;/h2&gt;&#10;&lt;h3 id="fediverse"&gt;Fediverse?&lt;/h3&gt;&#10;&lt;p&gt;Das &lt;a href="https://fediverse.info/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Fediverse nennt etwas Fediverse&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, wenn mehrere durchaus unterschiedliche Anbieter dasselbe Protokoll nutzen, um Nutzer dieser Anbieter frei miteinander interagieren zu lassen - und das in Bezug auf digitale Medien. Somit entsteht stark vernetzte aber dabei stets dezentrale Dienste, bei dem freie Anbieterwahl zum Konzept gehört. &lt;a href="https://www.theverge.com/24063290/fediverse-explained-activitypub-social-media-open-protocol" target="_blank" rel="noopener noreferrer" class="external-link"&gt;The Verge&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; geht hier einmal ins Detail und gibt einen Ausblick.&lt;/p&gt;&#10;&lt;h3 id="peertube"&gt;PeerTube?&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/PeerTube" target="_blank" rel="noopener noreferrer" class="external-link"&gt;PeerTube&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist eine dezentrale Videoplattform, bei der sich mehrere Server die Last teilen und auch Zuschauer beim Verbreiten von Videos helfen können. Grob gesagt steht &lt;em&gt;PeerTube&lt;/em&gt; zu &lt;em&gt;YouTube&lt;/em&gt; wie &lt;em&gt;Mastodon&lt;/em&gt; zum Kurznachrichtendienst &lt;em&gt;X&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h2 id="der-anlass"&gt;Der Anlass&lt;/h2&gt;&#10;&lt;p&gt;Dieser Artikel beginnt mit einer E-Mail.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;You&amp;rsquo;re receiving this non-automated email because you&amp;rsquo;re one of the handful of people who has a video hosted on this PeerTube instance. I have decided to shut down this service [&amp;hellip;]&amp;rdquo; - Tom, &lt;em&gt;scitech.video&lt;/em&gt; administrator&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Es tue ihm wirklich leid, aber der Weiterbetrieb der Instanz koste zu viel Zeit, Wartungsaufwand und Geld. Zudem sei die Plattform im Moment nur wenig genutzt, sodass der Aufwand unverhältnismäßig erscheine.&lt;/p&gt;&#10;&lt;p&gt;So weit, so verständlich.&lt;/p&gt;&#10;&lt;h2 id="toms-geschichte-scitechvideo"&gt;Toms Geschichte: &lt;em&gt;scitech.video&lt;/em&gt;&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-04-01-scitechvideo-stats.avif" alt="Image: scitech.video statistics as of Mid-March 2025: ~16k views total" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Da ich dort allerdings alle meine Videos hochgeladen und verlinkt hatte, war ich sehr an den Hintergründen interessiert. Also hakte ich nach. Meine Nachfrage wurde ausführlich und prompt beantwortet.&lt;/p&gt;&#10;&lt;h3 id="die-anfänge"&gt;Die Anfänge&lt;/h3&gt;&#10;&lt;p&gt;Tom setzte seine PeerTube-Instanz im Jahr 2019 auf. Ursprünglich, um eigene Inhalte zu teilen. Bereits wenig später öffnete er das Hosting-Angebot, wenn auch mit leichtem Unwohlsein, für andere.&lt;/p&gt;&#10;&lt;p&gt;Aus der Admin-Perspektive wirke so eine Videoplattform wie der &amp;ldquo;wilde Westen&amp;rdquo;. Seinem Angebot ein gesundes Wachstum angedeihen zu lassen, stellte sich als recht schwierig heraus - vor allem, weil viele Bots und Werbetreibende unterwegs sind.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Growing the service turned out to be challenging. Every time I opened registrations spambots started creating accounts easily and uploading videos with advertisements in the video descriptions [&amp;hellip;]&amp;rdquo; - Tom, &lt;em&gt;scitech.video&lt;/em&gt; administrator, via E-Mail&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h3 id="viele-bots-und-unerwünschte-user-unterwegs"&gt;Viele Bots und unerwünschte User unterwegs&lt;/h3&gt;&#10;&lt;p&gt;Diese unerwünschten Spammer, Scammer, Advertiser konnte er zwar stets davon abhalten, &lt;em&gt;scitech.video&lt;/em&gt; zu verseuchen. Dennoch bedeutete dies ständige Arbeit und manuelle Überwachung der automatischen Tools zum Ablehnen und Moderieren.&lt;/p&gt;&#10;&lt;p&gt;An dieser Stelle möchte ich Tom beipflichten. Ich habe im Schnitt etwa &lt;em&gt;340&lt;/em&gt; fehlgeschlagene und dann von mir weggeblockte Verbindungen externer IP-Adressen auf meinen Server pro Tag. Allein auf dem &lt;code&gt;ssh-Port&lt;/code&gt;. Und mein Server ist weder besonders groß, bekannt oder kann als lohnenswertes Ziel durchgehen. Ganz zu schweigen von zahllosen Versuchen, auf meine Gitea-Nutzeroberfläche zu kommen, die Website nach verborgenen Links zu crawlen und vieles mehr.&lt;/p&gt;&#10;&lt;p&gt;Für Betreiber eines Portals, welches auch externen Usern Schreibzugriff gibt (Videos, Beschreibungen, Tags, Kommentare), potenziert sich das Problem natürlich nochmal. Hier gehört anders als bei meiner Seite ja zum Konzept, sich zu beteiligen.&lt;/p&gt;&#10;&lt;h3 id="copyright"&gt;Copyright?&lt;/h3&gt;&#10;&lt;p&gt;Genau, grundsätzlich fällt aus Sicht des Plattformbetreibers erst einmal jedes Video unter das Urheberrecht oder verwandte Schutzrechte. Aber ist der Nutzer, der gerade dieses Video veröffentlichen will auch der Urheber? Oder hat dieser Jemand Nutzungsrechte erworben?&lt;/p&gt;&#10;&lt;p&gt;Je nach Land und zugehöriger Rechtslage müssen sich Admins mit diesem Thema sehr genau auseinandersetzen. Es können hohe Strafen sowohl für die Betreiber als auch für die User fällig werden, sollte eine Urheberrechtsverletzung vorliegen.&lt;/p&gt;&#10;&lt;p&gt;Ersteres ist zumindest in Deutschland einem &lt;a href="https://datenbank.nwb.de/Dokument/1055605/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Urteil des Bundesgerichtshofes vom 23.10.2024&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, wie folgt aufzulösen: &amp;ldquo;[&amp;hellip; sind] nach einem klaren Hinweis auf eine Rechtsverletzung die dort eingestellten Angebote im Rahmen des technisch und wirtschaftlich Zumutbaren auf gleichartige Verletzungen zu überprüfen und rechtsverletzende Inhalte zu sperren oder zu löschen&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Durch die starke Fokussierung der Instanzen auf bestimmte Themenbereiche fallen Verletzungen jedoch meist schnell auf - denn auch User können Beiträge &amp;ldquo;reporten&amp;rdquo;.&lt;/p&gt;&#10;&lt;h3 id="die-rechtslage-für-soziale-netzwerke"&gt;Die Rechtslage für soziale Netzwerke&lt;/h3&gt;&#10;&lt;p&gt;Einige Länder bereiten im Moment Gesetze vor, die Kinder und Jugendliche vor der Nutzung sozialer Netzwerke und damit verbundenen Gefahren schützen soll. Als Referenz hier ein &lt;a href="https://www.bzkj.de/bzkj/service/alle-meldungen/bzkj-schutzmassnahmen-und-staerkere-medienbildung-fuer-kinder-und-jugendliche-257566" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Link zur Bundeszentrale für Jugend- und Medienschutz&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Auch hier sind die Admins gefragt, diese Regeln umzusetzen - doch wie?&lt;/p&gt;&#10;&lt;p&gt;Dies jedenfalls fragt sich Tom angesichts der zunehmend vielfältiger werdenden Rechtsauffassungen unterschiedlicher Länder.&lt;/p&gt;&#10;&lt;h3 id="updates-und-wartung"&gt;Updates und Wartung&lt;/h3&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;PeerTube upgrades were a bit of a drag because everything was tightly coupled with their specific nginx configuration, with breaking changes that had to be adjusted every couple of minor versions.&amp;rdquo; - Tom, &lt;em&gt;scitech.video&lt;/em&gt; administrator, via E-Mail&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Die Arbeiten seien nicht besonders schwierig auszuführen, dennoch kosten sie eine Menge Zeit. Wie bei anderer Software auch könne es passieren, dass ein PeerTube-Server zum Beispiel nach Betriebssystemupdates sich nicht mehr hochfahren lässt oder es zu Inkompatibilitäten verschiedener Softwarestände oder Modulen untereinander kommt.&lt;/p&gt;&#10;&lt;p&gt;Aus Leistungsgründen stellt sich das Testen als schwierig heraus, sodass oft direkt in &amp;ldquo;Prod&amp;rdquo; gearbeitet wird. Im Fehlerfalle leidet der Livebetrieb.&lt;/p&gt;&#10;&lt;p&gt;Die Konfiguration sei relativ aufwändig und müsse auf die Hardware angepasst werden. Größere PeerTube-Updates erfordern auch hier Änderungen.&lt;/p&gt;&#10;&lt;h3 id="fazit"&gt;Fazit&lt;/h3&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;I&amp;rsquo;m also disillusioned about the fediverse generally as a communication medium. A constant battle against spam/LLMs, so much anger, so much focus on follows, likes and boosts.&amp;rdquo; - Tom, &lt;em&gt;scitech.video&lt;/em&gt; administrator, via E-Mail&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Tom ist frustriert, dass auch hier die Aufmerksamkeitsökonomie durchschlägt. Obwohl werbefrei gedacht und als kleine Graswurzelbewegung ausgerollt, werden aus Idealismus heraus erstellte Instanzen schnell für Werbung und Selbstdarstellung missbraucht. Die Kosten beliefen sich zudem auf mehrere hundert Dollar im Jahr, was die Kosten-Nutzen-Bilanz weiter verschlechtere.&lt;/p&gt;&#10;&lt;p&gt;Eine Lösung könne hier &amp;ldquo;die Rückkehr zu Web1.0 oder geschlossene Gemeinschaften&amp;rdquo; darstellen, wie es sie zum Beispiel bei &lt;em&gt;Meatspace&lt;/em&gt;, &lt;em&gt;Matrix&lt;/em&gt;, &lt;em&gt;Discord&lt;/em&gt; etc. bereits gebe.&lt;/p&gt;&#10;&lt;p&gt;Auch alle anderen Faktoren machen Tom den Weiterbetrieb seiner Instanz schwer: Die Nutzerbasis ist nie besonders groß geworden und es gibt nur wenige Menschen, die ihre Inhalte hier noch hochladen. Als Vater hat er nun viel weniger Zeit zur Verfügung und muss Projekte priorisieren. Somit beendet er nun seine immerhin sechs Jahre betriebene Reise als Anbieter im Fediverse.&lt;/p&gt;&#10;&lt;p&gt;Ich wünsche Tom für die Zukunft weiterhin viel Kraft, Idealismus und Enthusiasmus.&lt;/p&gt;&#10;&lt;h2 id="jans-geschichte-makertubenet"&gt;Jans Geschichte: &lt;em&gt;makertube.net&lt;/em&gt;&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-04-01-makertube-stats.avif" alt="Image: MakerTube statistics as of Mid March-2025: ~120k views total" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Da ich nun nach einer neuen Hosting-Plattform für meine Videos Ausschau halten musste, bemühte ich die Suche für &amp;ldquo;video-maker&amp;rdquo; auf der &lt;a href="https://joinpeertube.org/instances?profile=video-maker" target="_blank" rel="noopener noreferrer" class="external-link"&gt;PeerTube-Website&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Da meine Inhalte einen klaren Tech-Fokus haben und von Forschung, Produktentwicklung bis in den Vorserienbau reichen, war es nicht schwer, schnell passende Kandidaten ausfindig zu machen.&lt;/p&gt;&#10;&lt;p&gt;So kam ich dann zu meinem &lt;a href="https://makertube.net/c/cnc_corner/videos" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Kanal auf MakerTube&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Nach erfolgreicher Anmeldung schrieb ich eine Mail an die Admins mit ähnlichen Fragen, die ich Tom bereits gestellt hatte.&lt;/p&gt;&#10;&lt;h3 id="motivation---warum-makertube"&gt;Motivation - warum MakerTube?&lt;/h3&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Ich habe einen kleinen Youtube-Kanal und die permanente Werbung und der Fokus auf die großen Kanäle, Klickbait etc. und wie die Leute gezwungen werden sich der Content-Maschine anzupassen ging mir auf die Nerven. [&amp;hellip;]&amp;rdquo; - Jan, &lt;em&gt;MakerTube&lt;/em&gt; administrator, via E-Mail&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Jan war bereits im Fediverse unterwegs und beschäftigt sich auch beruflich mit Webdiensten, die stark skalieren müssen. Daher erschien es für ihn logisch, selbst eine Instanz aufzusetzen - dann aber natürlich mit den wirklich interessanten Themen dieser Welt. Es sei ja nicht als Konkurrenz zu YouTube gedacht und fokussiere sich daher auf Maker, Musikerinnen, Künstler etc.&lt;/p&gt;&#10;&lt;h3 id="ist-der-einstieg-leicht"&gt;Ist der Einstieg leicht?&lt;/h3&gt;&#10;&lt;p&gt;Es bedeute schon einiges an Aufwand, so eine Instanz hochzuziehen und performant zu halten. &lt;a href="https://framasoft.org/en/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Framasoft&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, welche die PeerTube-Software entwickeln, machten einen Superjob und unterstützen die Hoster-Community wo sie können.&lt;/p&gt;&#10;&lt;p&gt;Ich persönlich kann bestätigen, dass sich PeerTube in den neueren Versionen ganz anders anfühlt als noch vor ein paar Jahren und sich mit praktischen Features wie automatisch generierten Untertiteln immer näher an den Funktionsumfang von &lt;em&gt;YouTube&lt;/em&gt; heranrobbt.&lt;/p&gt;&#10;&lt;p&gt;Das Fediverse, so Jan, sei nach wie vor eine Nische. Daher falle das Wachstum seiner Instanz eher moderat und sehr organisch aus. &lt;em&gt;PeerTube&lt;/em&gt; sei im Moment noch etwas für enthusiastische Videomacher; am normalen Publikum laufen die Inhalte größtenteils ungesehen vorbei.&lt;/p&gt;&#10;&lt;h3 id="wie-groß-ist-der-zeitaufwand"&gt;Wie groß ist der Zeitaufwand?&lt;/h3&gt;&#10;&lt;p&gt;Jan legte den Fokus auf niedrige laufende Kosten bei gleichzeitiger Skalierbarkeit, falls die Sache plötzlich doch groß werden sollte. Jetzt, da das System reibungsfrei läuft, liege der Aufwand bei ein paar Stunden pro Woche für Moderation von Usern, Inhalten und Support.&lt;/p&gt;&#10;&lt;p&gt;Ausnahmen: Größere Updates, Upgrades und technische Probleme.&lt;/p&gt;&#10;&lt;h3 id="was-sagen-die-leute"&gt;Was sagen die Leute?&lt;/h3&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Es gibt positive Rückmeldungen und die Leute sind dankbar, dass ich die Seite betreibe und ich mir die Arbeit mache, aber ich würde sagen nicht sehr viele.&amp;rdquo; - Jan, &lt;em&gt;MakerTube&lt;/em&gt; administrator, via E-Mail&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Aber natürlich gebe es auch immer mal wieder Entrüstung darüber, dass das halt nicht YouTube wäre und nicht jeder sofort einen Account aufmachen könne.&lt;/p&gt;&#10;&lt;h3 id="wie-erkennt-ihr-urheberrechtsverletzungen"&gt;Wie erkennt ihr Urheberrechtsverletzungen?&lt;/h3&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Bezüglich Copyright gibt es da keine direkten Hilfsmittel. Es gibt die Report-Funktion an den Videos, um auf Verstöße gegen Copyright oder Instanzregeln hinzuweisen. Das wird auch von Benutzern genutzt.&amp;rdquo; - Jan, &lt;em&gt;MakerTube&lt;/em&gt; administrator, via E-Mail&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Verantwortung und Haftung lägen bei so kleinen Anbietern wie &lt;em&gt;MakerTube&lt;/em&gt; in erster Linie beim User. Wenn allerdings auf Verstöße hingewiesen wird, müssen die Admins tätig werden und Inhalte löschen. Auf &lt;em&gt;MakerTube&lt;/em&gt; sei dies allerdings im Alltag kein Problem: Die Registrierungen neuer User wird genau geprüft und im Zweifel werde nachgefragt, um welche Inhalte es gehe. Bei den meisten Verletzungen gehe es um Hintergrundmusik der Videos, da gebe es scheinbar weniger Bewusstsein für Urheberrecht wie bei Uploads von Videokopien.&lt;/p&gt;&#10;&lt;h3 id="und-wie-finanziert-sich-das-ganze"&gt;Und wie finanziert sich das Ganze?&lt;/h3&gt;&#10;&lt;p&gt;MakerTube finanziert sich über &lt;a href="https://makertube.net/about/instance/support" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Spenden&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Gerade zu Anfang ging laut Jan viel Zeit dafür drauf, dies den Leuten klar zu machen und genügend Budget für einen flüssigen Betrieb zu erhalten. Eine Videoplattform sei ressourcenintensiv: Viel CPU-Power für Transkodierung und Streaming, einen Haufen Plattenplatz für die ganzen Assets bei gleichzeitig hohen Anforderungen an die Bandbreite, gerade in &amp;ldquo;Stoßzeiten&amp;rdquo;.&lt;/p&gt;&#10;&lt;h3 id="fazit-1"&gt;Fazit&lt;/h3&gt;&#10;&lt;p&gt;Ich bin mit &lt;em&gt;MakerTube&lt;/em&gt; bis jetzt zufrieden: Transkodierungen gehen flott, Videos laufen flüssig. Sogar der &lt;em&gt;Heise-Verlag&lt;/em&gt; hat nun ein erstes Video auf die Instanz hochgeladen. Es ist allerdings deutlich schwieriger, ohne &amp;ldquo;Fanbase&amp;rdquo; auf die Landing Page zu kommen, was bei &lt;em&gt;scitech.video&lt;/em&gt; für mich nie ein Problem darstellte 😅.&lt;/p&gt;&#10;&lt;p&gt;Lasst mich mit einem weiteren Zitat von der Site selbst schließen:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;MakerTube is here to stay.&amp;rdquo; - &lt;em&gt;MakerTube&lt;/em&gt;, March 2025, about &lt;em&gt;MakerTube&lt;/em&gt;.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Danke, Jan. Auf die Zukunft!&lt;/p&gt;&#10;</description></item><item><title>Gitea: Suchmaschinenindexierung stoppen</title><link>https://blog.schallbert.de/gitea-search-indexation/</link><pubDate>Wed, 12 Mar 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/gitea-search-indexation/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-03-12-gitea-indexation-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: How gitea recommends to remove search indexation"&#10; title="Gitea: Suchmaschinenindexierung stoppen" /&gt;&#10;&lt;p&gt;Nach ewigen Zeiten habe ich nur zum Spaß mal wieder einen Ausflug auf die Google Search Console gemacht. Normalerweise interessiert die mich überhaupt nicht, weil ich selbst Google gar nicht nutze und davon ausging, dass die Suchmaschinen ihre Arbeit aus Eigeninteresse schon gut machen würden.&lt;/p&gt;&#10;&lt;p&gt;Ich war etwas irritiert, als ich feststellte, dass es das Gros meiner Seiten nicht mal durch den Indexer geschafft hat.&lt;/p&gt;&#10;&lt;h2 id="das-problem"&gt;Das Problem&lt;/h2&gt;&#10;&lt;p&gt;Wie sollen denn Leute meine Webpräsenz finden? Selbst wenn sie die korrekten Suchbegriffe wüssten, würden sie über Google kaum etwas angezeigt bekommen. Immerhin stand klar drin, dass keiner meiner Blogartikel im Indexer vorkam. Auf meiner Gitea-Instanz hingegen waren mehrere tausend Seiten dem Indexer bekannt. Ein klares Missverhältnis. Wieso überhaupt erzeugt Gitea ein so hohes Dateiaufkommen?&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-03-12-searchconsole.avif" alt="Image: Google Search console view for git.schallbert.de. Thousands of pages are crawled that a real user will never be interested in." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Googles &lt;a href="https://en.wikipedia.org/wiki/Web_crawler" target="_blank" rel="noopener noreferrer" class="external-link"&gt;crawler&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; (ein Programm, welches das Netz nach Inhalten durchsucht und für die Suche indexierbar macht) findet anscheinend jeden noch so kleinen Commit in öffentlichen Repositories sowie die dahinter liegenden Dateien. Auch Automationsläufe und andere Metadaten werden indexiert.&lt;/p&gt;&#10;&lt;p&gt;Das ist natürlich vollkommen unnötig und verbraucht mir an einer Stelle Energie, die ich viel lieber in der Präsentation meines Blogs hätte. Also muss eine Lösung her.&lt;/p&gt;&#10;&lt;h2 id="lösungsansätze"&gt;Lösungsansätze&lt;/h2&gt;&#10;&lt;p&gt;Mir fielen direkt ein paar Möglichkeiten ein:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Den Crawler für jedes Repository so konfigurieren, dass nur die oberste Ordnerebene durchsucht wird.&lt;/li&gt;&#10;&lt;li&gt;Suchindexierung für &lt;code&gt;git.schallbert.de&lt;/code&gt; komplett abschalten.&lt;/li&gt;&#10;&lt;li&gt;Per Search Console so lange Korrekturen fahren, bis der Indexer auf meinen Blogbeiträgen korrekte Zuordnungen anzeigt.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Die naheliegendste Lösung für mich war, den Indexer für meine Gitea-Instanz komplett abzuschalten. Korrekturschleifen greifen bei Googles Crawler erst Tage oder Wochen später und waren mir zu langwierig. Eine eigene &lt;code&gt;robots.txt&lt;/code&gt; für jedes Repository hielt ich für zu aufwändig.&lt;/p&gt;&#10;&lt;h2 id="meine-umsetzung"&gt;Meine Umsetzung&lt;/h2&gt;&#10;&lt;p&gt;Hierfür nahm ich die &lt;a href="https://docs.gitea.com/administration/search-engines-indexation" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Gitea-Dokumentation&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zur Hand. Der Vorschlag mit einer &lt;code&gt;robots.txt&lt;/code&gt; leuchtete mir sofort ein, ebenso ihr Inhalt:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;User-agent&lt;/span&gt;: *&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;Disallow&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hiermit wird Crawlern von jedem Anbieter &lt;code&gt;*&lt;/code&gt; gesagt, dass ab dem Stammverzeichnis &lt;code&gt;/&lt;/code&gt; überhaupt nicht indexiert werden soll.&lt;/p&gt;&#10;&lt;p&gt;Leider nur war mir überhaupt nicht klar, wie Gitea diese Datei in seiner Instanz bereitstellt:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;To make Gitea serve a custom robots.txt (default: empty 404) for top level installations, create a file with path &lt;code&gt;public/robots.txt&lt;/code&gt; in the &lt;a href="https://docs.gitea.com/administration/customizing-gitea" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;code&gt;custom&lt;/code&gt; folder or &lt;code&gt;CustomPath&lt;/code&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h3 id="giteas-konfigurationsverzeichnis"&gt;Giteas Konfigurationsverzeichnis&lt;/h3&gt;&#10;&lt;p&gt;Also probierte ich ein Wenig herum. Erstellte mal einen &lt;code&gt;public&lt;/code&gt;-Ordner mit entsprechender Datei, dann einen &lt;code&gt;custom&lt;/code&gt;-Ordner auf verschiedenen Ebenen innerhalb der Gitea-Ordnerstruktur. Jedes mal startete ich den Container neu und schaute nach, ob die robots-Datei auch auf dem Server erschien.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-03-12-whererobots.avif" alt="Image: Where I put robots.txt in Gitea&amp;#39;s folder tree" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Nichts.&lt;/p&gt;&#10;&lt;p&gt;Doch dann löste ich mich von den Ordnernamen und packte die Datei schlicht in das Konfigurationsverzeichnis von Gitea - die niedrigste Ebene, auf der &lt;em&gt;gitea&lt;/em&gt; als Ordnername schon gar nicht mehr vorkommt. Und dies war die Lösung.&lt;/p&gt;&#10;&lt;h3 id="test"&gt;Test&lt;/h3&gt;&#10;&lt;p&gt;Nach Neustart konnte ich die Datei im Browser erfolgreich anzeigen.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-03-12-robotsolution.avif" alt="Image: Browser successfully loaded my robots.txt for the Gitea instance" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Inzwischen beginnt auch Google, die unerwünschten Seiten aus dem Indexer zu entfernen. Meine Blogartikel werden über die nächsten Wochen hoffentlich auch in der Google-Suche verfügbar.&lt;/p&gt;&#10;</description></item><item><title>Graustufenbilder auf Dibond</title><link>https://blog.schallbert.de/halftone-image-on-dibond/</link><pubDate>Tue, 25 Feb 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/halftone-image-on-dibond/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-02-25-bloglogo-stipple-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: halftone image engraved with my CNC"&#10; title="Graustufenbilder auf Dibond" /&gt;&#10;&lt;p&gt;Im letzten Artikel hatte ich Dibond (Aluminium-Sandwichplatte) als Material zum Zerspanen wiederentdeckt. Ich setze es aus Umweltgründen - Verbundmaterialien sind nichts, was ich für besonders nachhaltig oder recyclebar halte - immer seltener ein. Dennoch üben die wunderschönen Oberflächen immer wieder eine gewisse Anziehungskraft auf mich aus. Nun denn - zur Sache!&lt;/p&gt;&#10;&lt;h2 id="bloglogo-als-punktwolke"&gt;Bloglogo als Punktwolke&lt;/h2&gt;&#10;&lt;p&gt;Inzwischen war ich in der Lage, ein von Stipplegen erzeugtes Bild mal auf die Fräse zu werfen. Dafür musste ich ein im Vorgängerartikel &lt;a href="https://blog.schallbert.de/halftone-cnc/"&gt;Graustufenbilder mit der CNC fräsen&lt;/a&gt; noch vorhandenes Problem lösen: In meinem meistgenutzten CAM-Werkzeug nämlich können Fräspfade für punktebasierte Halbtonbilder kaum erstellt werden, da jeder der über 2200 Punkte einzeln mit einer Fräsoperation versehen werden müsste.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;CADasCAM&lt;/em&gt; hingegen ermöglicht es, ganze Gruppen von Elementen ein- und derselben Fräsaktion hinzuzufügen. Nun fragte ich mich, mit welchem Fräser ich in Dibond am besten arbeiten solle, denn ich habe ja nur eine sehr dünne Deckschicht zur Verfügung.&lt;/p&gt;&#10;&lt;h3 id="simulation-radienfräser"&gt;Simulation Radienfräser&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-02-25-r2-ballnose-approximation.jpg" alt="Image: my drawing for V-cut to radius cutter approximation" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich entschied mich für einen Radienfräser mit Halbrundkopf. Für diesen hat das CAM allerdings keine Berechnungsvorschrift, sodass er sich nicht ohne Weiteres auswählen lässt. Nach Rücksprache mit dem Hersteller kam die Idee auf, den Radienfräser durch einen V-cut mit Abflachung an der Spitze für das CAM zu simulieren, tatsächlich auf der Fräse aber den Kugelkopf einzusetzen.&lt;/p&gt;&#10;&lt;p&gt;Um hier passende Werte zu erhalten, fertigte ich ein paar Zeichnungen an. In der Tabelle unten sind Näherungswerte festgehalten für den Falle, dass die Eintauchtiefe &lt;code&gt;0.3mm&lt;/code&gt; nicht überschreitet. Nun konnte ich die Pfadberechnung im CAM durchführen und sogar eine Tiefenbegrenzung von &lt;code&gt;0.22mm&lt;/code&gt; einhalten. Die flachsten Bereiche haben eine Gravurtiefe von lediglich &lt;code&gt;0.10mm&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Bei der Pfadberechnung ist zu beachten, dass die Rückzugshöhen möglichst gering ausfallen sollten. Dann muss die Z-Achse der Maschinen nicht für jeden Punkt so weit hochfahren. Ich halte einen Wert von &lt;code&gt;0.5mm&lt;/code&gt; für angemessen.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Radienfräser&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Simulation durch V-Cut&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;r=3mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;140°, 1.0mm Abflachung&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;r=2mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;140°, 0.5mm Abflachung&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="fräsvorgang"&gt;Fräsvorgang&lt;/h3&gt;&#10;&lt;p&gt;Die Motivgröße für die Gravur beträgt &lt;code&gt;180x180mm&lt;/code&gt; und ich fräse mit folgenden Werten: &lt;code&gt;Radienfräser 2s r3 6mm, S20000, F1800&lt;/code&gt;. Ich durchsteche die Schutzfolie der Dibondplatte, deren Dicke ich nicht kompensiere. Ich verwende eine geringe Menge Schmiermittel zur Verbesserung der Schnittqualität. Nach knapp &lt;code&gt;11&lt;/code&gt; Minuten ist der Fräsvorgang beendet.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-02-25-r2-bloglogo-stipple.avif" alt="Image: Bloglogo as halftone dot image in Dibond, black" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="beobachtungen"&gt;Beobachtungen&lt;/h3&gt;&#10;&lt;p&gt;Das Motiv ist in diesem Versuch schon einmal viel deutlicher zu erkennen. Leider habe ich dennoch klar ein Problem mit der Oberflächenebenheit. So ist im mittleren Bereich rechts beispielsweise zu tief graviert (Aufbauschneide - siehe Video - dort ist die Fräse zum Schluss unterwegs), während die Graviertiefe ganz oben rechts und oben mittig nicht ausreichend erscheint.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Fräse und Fräser haben überhaupt kein Problem mit dem Material. Der Gravurvorgang klingt ein wenig wie die Arbeit mit der Ständerbohrmaschine, unterbrochen von Positioniergeräuschen der Schrittmotoren.&lt;/li&gt;&#10;&lt;li&gt;Die für Dibond verwendete Aluminiumsorte &lt;em&gt;AlMg1&lt;/em&gt; ist weich und zäh. Sie eignet sich nicht gut zum Zerspanen.&lt;/li&gt;&#10;&lt;li&gt;Fräser tendieren hier zum Erzeugen von &lt;a href="https://en.wikipedia.org/wiki/Built_up_edge" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Aufbauschneiden&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, welche den Fräserradius vergrößern und die Schnittqualität verschlechtern.&lt;/li&gt;&#10;&lt;li&gt;Die Schutzfolie verhindert Ausrisse und Spanränder.&lt;/li&gt;&#10;&lt;li&gt;Aufbringen von etwas Schmiermittel ist eine gute Idee.&lt;/li&gt;&#10;&lt;li&gt;Selbst auf einem Vakuumtisch ruiniert jedes Staubkorn unter dem Werkstück die Fräsarbeit. Wenige hundertstel Millimeter Höhendifferenz haben große Auswirkung auf die Punktgröße und führen zu Verfälschungen.&lt;/li&gt;&#10;&lt;li&gt;Das Maschinenbett muss absolut eben sein oder die Maschine muss über eine hochwertige, aktive Höhenkompensation verfügen; z.B. mittels vorheriger Oberflächenvermessung durch einen &lt;a href="https://blog.schallbert.de/touching-off/"&gt;Kantenfinder&lt;/a&gt;. Meine Steuersoftware unterstützt das Verfahren sogar unter dem Namen &lt;em&gt;ZheightComp&lt;/em&gt;.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="fazit"&gt;Fazit&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Ich mag Dibond als Material sehr, aber es scheint meine Zuneigung nicht zu erwidern.&lt;/li&gt;&#10;&lt;li&gt;Mit meinen technischen Mitteln bekomme ich Gravuren nicht mit wiederholbar in guter Qualität hin.&lt;/li&gt;&#10;&lt;li&gt;Vielleicht sollte ich stattdessen Plastik oder Holz verarbeiten. Oder einen Laser verwenden&amp;hellip;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Zum Abschluss noch ein kurzes Zeitraffer-Video des Fräsvorganges.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/qTd6Ad2QStCp5AiWmrHZem" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Graustufenbilder mit der CNC herstellen - Teil2&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="saturn-als-ascii-art"&gt;Saturn als ASCII-Art&lt;/h2&gt;&#10;&lt;p&gt;Halbtonbilder kann man statt mit Linien, Wellen, Punkten natürlich auch mit Mustern herstellen. Als Beispiel habe ich mal das Bild des Planeten Saturn von &lt;a href="https://asciiart.website/index.php?art=nature/astronomy" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Christopher Johnsons ASCII Art&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; für die Fräse aufbereitet.&lt;/p&gt;&#10;&lt;h3 id="cadcam"&gt;CAD/CAM&lt;/h3&gt;&#10;&lt;p&gt;Für das richtige Größenverhältnis wird eine Schriftart benötigt, bei der jedes Zeichen dieselbe Setzbreite besitzt. Ich wähle die TrueType-Schriftart &lt;em&gt;Courier New&lt;/em&gt; und passe Schriftgröße und Zeilenabstand so an, dass das Motiv auf mein Material passt und die Gravur nicht die Deckschicht perforiert.&lt;/p&gt;&#10;&lt;p&gt;Dies zwingt mich zur Verwendung eines &lt;code&gt;1/8&amp;quot; Gravurstichel mit 36° Spitzenwinkel und 0.1mm Abflachung&lt;/code&gt;. Ich hätte hier einen Radienfräser bevorzugt: Im weichen Dibond kommt es bei spitzwinkligen Sticheln zu Materialaufwurf und -Verschmierung. Dies führt zu unsauberen Linien und einer rauen Oberfläche. Da das Werkstück sehr klein ist, würde die Schrift jedoch viel zu breit ausfallen.&lt;/p&gt;&#10;&lt;p&gt;Die Pfadberechnung zeigt, dass ich auf dem nur &lt;code&gt;80x80mm&lt;/code&gt; großen Werkstück Fettdruck wählen muss, um meine Mindesttiefe von &lt;code&gt;0.08mm&lt;/code&gt; überhaupt zu erreichen.&lt;/p&gt;&#10;&lt;h3 id="fertigung"&gt;Fertigung&lt;/h3&gt;&#10;&lt;p&gt;Ich habe das Bild vom Saturn zweimal gefertigt: Auf &lt;code&gt;80x80mm&lt;/code&gt;, um die maximale Auflösung im Material zu testen und auf &lt;code&gt;180x180mm&lt;/code&gt; (Abbildung unten) zum Prüfen der Kontrastwirkung.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-02-25-halftone-saturn.avif" alt="Image: Halftone image of Saturn from my CNC machine. Image rights: asciiart.website" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Ich bin zufrieden mit dem Ergebnis. Der Kontrast ist hoch und die silberweiße Oberfläche des Aluminium(oxyds) sieht auf der mattschwarzen Lackierung richtig edel aus.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/wxcEMorVuY3hVZkseFjXyU" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Graustufenbilder mit der CNC fertigen: Teil3&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="qr-codes"&gt;QR-Codes&lt;/h2&gt;&#10;&lt;p&gt;Wenn ich schon einmal mit Dibond arbeite: Ich probiere gleich noch aus, ein paar QR-Codes mit einem Radienfräser einzubringen.&#10;Dieses Mal verwende ich verschiedene Maschinen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-02-25-qr-dibond.avif" alt="Image: QR-codengrave tests in dibond with a radius cutter" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="auf-der-hobbyline"&gt;Auf der Hobbyline&lt;/h3&gt;&#10;&lt;p&gt;In schwarz graviere ich auf der mir zur Verfügung gestellten Hobbyline im übertakteten Gravurmodus. Ich fixiere das Werkstück mit einigen Punkten Doppelklebeband. Ich fräse trocken ohne Schmiermittel. Beobachtungen:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Die Gravurtiefe scheint mit zunehmender Linienlänge zuzunehmen (Aufbauschneide, Fräser schmiert ins Material)&lt;/li&gt;&#10;&lt;li&gt;Teilweise wölbt sich die Lackoberfläche am Fräsrand nach oben&lt;/li&gt;&#10;&lt;li&gt;Sehr unsaubere Fräskanäle, starke Richtungsabhängigkeit&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="auf-der-basicline"&gt;Auf der Basicline&lt;/h3&gt;&#10;&lt;p&gt;Hier nutze ich natürlich meinen Vakuumtisch und profitiere vom viel stabileren Aufbau. Ich verwende eine geringe Menge Schmiermittel. War gespannt, ob die das weiße Material trotzdem ausreichende Kontraste zulässt. Beobachtungen:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Auch meine &lt;a href="https://blog.schallbert.de/projects/qr-codengrave/"&gt;QR-codeengrave&lt;/a&gt; Software muss richtig bedient werden. Ansonsten verschieben sich wie hier Nullpunkte und Größenverhältnisse. Folge: Mein Handy erkennt den QR-Code nicht 😐&lt;/li&gt;&#10;&lt;li&gt;Die viel bessere Niederhaltung verhindert starke Unterschiede in der Fräskanalbreite.&lt;/li&gt;&#10;&lt;li&gt;Dennoch sieht man die Austauchpunkte der Maschine und manchmal auch mitgeschleifte Späne.&lt;/li&gt;&#10;&lt;li&gt;Die Fräsqualität ist noch immer nicht befriedigend.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="fazit-1"&gt;Fazit&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-11-01_smallparts-thumb.jpg" alt="Image: single-flute cutter for PMMA/Acrylics." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;In Zukunft muss ich Dibond ähnlich behandeln wie Acrylglas: Sehr scharf geschliffene Fräser verwenden, die das Material ordentlich schneiden. Idealerweise Einschneider mit Habichtschnabel und Eckenfase (weiche Kanten), zentrumsschneidend, dazu ordentlich Drall. Z.B. &lt;a href="https://shop.vhf.de/articleGroups/Einzahnfraeser-mit-Stirnradius-W_F_ES_SCR.htm" target="_blank" rel="noopener noreferrer" class="external-link"&gt;diese hier (Link zu vhf)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. So wird - glaube ich - eine Materialverdrängung in das noch weichere Kernmaterial vermieden und Späne außerdem im Spankanal nach oben gefördert, sodass sie nicht anhaften können oder ein zweites Mal unter den Fräser geraten.&lt;/p&gt;&#10;&lt;p&gt;Zudem sollte ich mit zügigen Vorschubwerten arbeiten. Dann heizt sich das Material nicht zu stark auf. Die Verwendung von ein wenig Schmiermittel sowie das Aufbringen von Schutzfolie trägt zu besserem Fräsergebnis bei. Weitere Verarbeitungshinweise gibt es beispielsweise &lt;a href="https://www.cnc-aus-holz.at/index.php?thread/2397-aluverbundplatte-dibond-gravieren/&amp;amp;postID=27536#post27536" target="_blank" rel="noopener noreferrer" class="external-link"&gt;im CNC-aus-Holz-Forum&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Stöbern kann sich lohnen.&lt;/p&gt;&#10;</description></item><item><title>Graustufenbilder mit der CNC fräsen</title><link>https://blog.schallbert.de/halftone-cnc/</link><pubDate>Mon, 27 Jan 2025 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/halftone-cnc/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-01-27_bloglogo-thumb.avif"&#10; class="post-cover"&#10; alt="Image: TSP-paths of my blog&amp;#39;&amp;#39;s logo"&#10; title="Graustufenbilder mit der CNC fräsen" /&gt;&#10;&lt;p&gt;Bis jetzt habe ich Bilder stets als Gravuren in Volltonfarben erstellt. Damit lassen sich Texte wie &lt;a href="https://blog.schallbert.de/negative-carving-with-estlcam/"&gt;diesen hier&lt;/a&gt;, aber auch &lt;a href="https://blog.schallbert.de/engrave-multicolor/"&gt;Zeichnungen&lt;/a&gt; und &lt;a href="https://blog.schallbert.de/makerfaire-ruhr/#vorbereitung"&gt;Bilder&lt;/a&gt; kontrastreich und hübsch fertigen.&lt;/p&gt;&#10;&lt;h2 id="das-ziel"&gt;Das Ziel&lt;/h2&gt;&#10;&lt;p&gt;Bei der Volltondarstellung können viele Details nicht abgebildet werden. Nun möchte ich aber ein echtes Foto ins Material bringen. Idealerweise soll dies zügig, ohne manuelle Nacharbeit und mit hoher Bildqualität passieren.&lt;/p&gt;&#10;&lt;p&gt;Mein CAM-Werkzeug stellt hierfür eine &amp;ldquo;Halbtonbild&amp;rdquo;-Funktion zur Verfügung, mit der ich im Folgenden herumexperimentiere. Auch hier ist es wieder so, dass selten direkt gute Ergebnisse aus der Maschine fallen. Außerdem sind die Erzeugung solcher Abbildungen sehr zeitintensiv, und Fehlversuche folglich &amp;ldquo;teuer&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Daher gebe ich am Ende des Artikels ein paar Tipps und Tricks, mit deren Hilfe recht schnell gute Ergebnisse erzielt werden können.&lt;/p&gt;&#10;&lt;h2 id="ein-wenig-farbwissen-rasterverfahren"&gt;Ein wenig Farbwissen: Rasterverfahren&lt;/h2&gt;&#10;&lt;p&gt;Das Problem, einen ganzen Farbraum mit nur wenigen verfügbaren Farben abbilden zu wollen, ist schon hunderte Jahre alt. Während Farbverläufe durch Mischen von Farben in der analogen Welt und auch am Bildschirm mittels unterschiedlicher Luminanzwerte der drei Farbkanäle in Pixeln hervorragend dargestellt werden können, stehen bei längst nicht jeder Anwendung eine große Anzahl Farben zur Verfügung.&lt;/p&gt;&#10;&lt;p&gt;Drucker zum Beispiel&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. Sie haben meist nur die drei Grundfarben &lt;code&gt;cyan (C)&lt;/code&gt; - ein Türkiston, &lt;code&gt;magenta (M)&lt;/code&gt; - ein Pinkton, &lt;code&gt;yellow (Y)&lt;/code&gt; - gelb sowie &lt;code&gt;key (K)&lt;/code&gt; - meist Schwarz - zur Verfügung. Hieraus müssen sie dann die Illusion aller möglicher Farben erzeugen, denn sie können die Pigmente wegen ihrer hohen Deckkraft nicht mischen. Ansonsten würde der zuletzt aufgebrachte Farbton dominieren oder die Farbe würde schlicht verlaufen. Hieraus ergibt sich die Notwendigkeit, Rasterverfahren einzusetzen.&lt;/p&gt;&#10;&lt;h3 id="halftones-halbtonbild"&gt;Halftones (Halbtonbild)&lt;/h3&gt;&#10;&lt;p&gt;Eines dieser Rasterverfahren heißt &lt;em&gt;halftone&lt;/em&gt;. Hierbei wird durch Farbpunkte oder -Muster unterschiedlicher Strichstärke ein &lt;a href="https://en.wikipedia.org/wiki/Halftone" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Farbton simuliert&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, welchen das Auge ab einem gewissen Abstand nicht mehr auflösen kann und daher gemeinsam mit dem umgebenen Freiraum und gegebenenfalls anderen Farbpunkten zu einem Tonwert &amp;ldquo;mittelt&amp;rdquo;.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-01-27_bloglogo_9600dots.avif" alt="Image: 9600 dots stipple image of my blog&amp;#39;s logo" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Dieses Verfahren eignet sich für die &lt;a href="https://hackaday.com/2011/07/28/creating-halftone-pictures-with-a-cnc-machine/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Fertigung mittels CNC-Fräse&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, sofern die Oberfläche des zu bearbeitenden Werkstücks eine andere Farbe besitzt als das Kernmaterial.&lt;/p&gt;&#10;&lt;p&gt;Für Halftones existieren viele verschiedene Erzeugungsverfahren, bei denen sich Anordnung der Punkte, Striche, Musterform usw. teils stark unterscheiden. Auf der CNC bestimmen das verwendete Gravurmaterial, die benötigte Auflösung und das gewünschte Werkzeug das Verfahren, da sich nicht mit jeder Kombination eine gute Qualität erreichen lässt.&lt;/p&gt;&#10;&lt;h3 id="dithering"&gt;Dithering&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Dither#Digital_photography_and_image_processing" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Dithering&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; wird eher bei der Darstellung auf Bildschirmen verwendet. Hierbei variiert nicht wie beim Halbtonverfahren Stärke bzw. Größe von Farbpunkten oder Mustern, sondern die räumliche Verteilung andersfarbiger Punkte, welche in die Grundfarbe gesprenkelt werden. Dadurch entsteht der Eindruck einer Mischfarbe.&lt;/p&gt;&#10;&lt;p&gt;Auch hier gibt es wieder verschiedene Erzeugungsverfahren: Diffusion, Rauschen, Muster, &lt;a href="https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Floyd-Steinberg-Algorithmus&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&amp;hellip;&lt;/p&gt;&#10;&lt;h2 id="materialauswahl"&gt;Materialauswahl&lt;/h2&gt;&#10;&lt;p&gt;Hier habe ich eine Menge ausprobiert. Am Besten eignen sich Materialien mit homogener Zusammensetzung und hohem Kontrast zwischen Oberflächen- und Kernfarbe. Zudem sollte die Schichtdicke des Oberflächenmaterials nicht zu groß ausfallen, da sie komplett durchstochen werden muss und dann schnell sehr große Einzelpunkte erforderlich werden. Die Oberfläche ist idealerweise mattiert, denn Seiden- Metall- oder Hochglanz erzeugen eine Blickwinkelabhängigkeit, bei der die Erkennbarkeit des Motivs leidet.&lt;/p&gt;&#10;&lt;p&gt;Außerdem sollte das Material komplett plan zur Fräsfläche sein, da die Punktgröße sonst unerwünscht stark variiert.&lt;/p&gt;&#10;&lt;h3 id="geeignete-materialien-"&gt;Geeignete Materialien 👍&lt;/h3&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Kernmaterial&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Beschichtung&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Besonderheit&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Mitteldichte Faserplatte&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Grundierung + Lack&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Fräsbahn nicht überlappen&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Aluverbundmaterial&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lack oder Folie&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Max. 0.18mm tief fräsen&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Acrylglas (PMMA)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lack oder Folie&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;XT verwenden (Dickentoleranz)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Am wenigsten Aufwand macht mir die Verarbeitung von fertig lackiertem Aluverbundmaterial. Hier ist praktisch keine Vor- und Nacharbeit erforderlich. Es ist aber darauf zu achten, die Frästiefe auf &lt;code&gt;0.18mm&lt;/code&gt; zu begrenzen. Sonst wird der PU-Kern deformiert oder es entstehen Ausrisse.&lt;/p&gt;&#10;&lt;p&gt;Das Aufbringen einer Folie kann eine gute Lösung sein, wenn man den Zeitaufwand eines Farbauftrags scheut. Ich selbst bin leider nicht besonders geschickt im Umgang mit Folie. Ständig erzeuge ich Luftblasen oder Einschlüsse kleinster Staubkörnchen. Nach dem Fräsvorgang sind die dadurch entstandenen Defekte im Höhenprofil deutlich anhand zu großer Löcher im Material zu erkennen.&lt;/p&gt;&#10;&lt;h3 id="ungeeignete-materialien-"&gt;Ungeeignete Materialien 👎&lt;/h3&gt;&#10;&lt;p&gt;Die hier aufgelisteten Materialien eignen sich meiner Meinung nach so nicht gut zum Erzeugen von Halbtonbildern. Ich habe ein paar Bilder misslungener Versuche hier abgelegt. Oft liegt ein Grund für Fehlschläge in einer für das Material zu hoch gewählten Bildauflösung.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Kernmaterial&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Beschichtung&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Grund&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Mitteldichte Faserplatte&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Furnier&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Zu hohe Schichtdicke&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Mitteldichte Faserplatte&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Metallfolie&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Papierkern der Metallfolie ist weiß&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Aluverbundmaterial&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Metall, gebürstet&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Material &amp;ldquo;matscht&amp;rdquo;, Auflösung schlecht&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Aluverbundmaterial&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Keine, PU-Kern&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Alu in Kern gedrückt, Auflösung mies&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Weiche Alu-Legierungen&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lack oder Folie&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Aluminium verschmiert, Auflösung mies&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-01-27-3guys_perfect.avif" alt="Image: 3 guys als halftone image on metalized MDF" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Hier experimentiere ich mit der Bildpunktdichte. Im unteren Bereich habe ich sie zu eng angeordnet, sodass sich die Beschichtung beim Fräsen löst. Auf meiner Gravur-CNC benötigt ein solches Bild auf einer Platte mit den Maßen&lt;code&gt;15x10cm&lt;/code&gt; trotz Übertaktung etwa &lt;code&gt;15 Minuten&lt;/code&gt;. Das Material ist wegen des Metallglanzes, der Ausrissgefahr, einer kaum erreichbaren Flächenverleimung und der hohen Schichtdicke nicht gut für solch hohe Auflösungen geeignet.&lt;/p&gt;&#10;&lt;h2 id="halbtonbilder-anforderungen-an-die-cnc"&gt;Halbtonbilder: Anforderungen an die CNC&lt;/h2&gt;&#10;&lt;p&gt;Das Problem hängt hier ausnahmsweise mal nicht mit Haltekräften, Fräskräften oder Schwierigkeiten beim Niederhalten zusammen. Im Gegenteil, beim Fräsen von Bildern werden außerordentlich geringe Kräfte aufgebracht, und die zumeist in Z-Richtung, wo die Maschinen eine Stabilitätsreserve besitzen.&lt;/p&gt;&#10;&lt;p&gt;Die Komplexität der Jobs stellt das Problem dar. Das Portal muss große Strecken zurücklegen und auf dem Weg ständig bremsen und beschleunigen. Endstufen, Schrittmotoren und Lager der Linearspindeln sind hierdurch stark belastet.&lt;/p&gt;&#10;&lt;h3 id="wellenstrichelinien"&gt;Wellen/Striche/Linien&lt;/h3&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-01-27-anywhereamps_halftonegrid.avif" alt="Image: Halftone wave-grid image of AnywhereAmps1.2 compared to original image" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Im Linienverfahren fährt die Maschine ein Bild zeilenweise ab. Helligkeitsinformationen werden dabei üblicherweise per Frästiefe übertragen. Damit ergeben z.B. besonders dunkle Bereiche breitere Schnitte, welche dann im Schatten liegen. Dieses Verfahren eignet sich prima bei Fräsungen in Schaumstoffen, MDF und Acrylglas. Sie lassen sich außerdem recht zügig durchführen, da die Maschine im Materialeingriff bleibt und lediglich für Zeilensprünge Positioniervorgänge erforderlich sind.&lt;/p&gt;&#10;&lt;h3 id="punktverfahren"&gt;Punktverfahren&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-01-27-bloglogo_stipple.avif" alt="Image: bloglogo made of 2000 Vonoroi stipples" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Hier wird die Größe von Punkten verändert, sodass mal größere und mal kleinere Vertiefungen im Material entstehen.&lt;/p&gt;&#10;&lt;p&gt;Halbtonbilder im Punktverfahren benötigen verhältnismäßig viel Zeit auf der Maschine. Was mit einem Hobbylaser in einer halben Stunde vonstatten gehen kann, dauert mit einer CNC-Maschine bei gleicher Größe und Auflösung locker mal zwei Stunden (auch hier gibt es &lt;a href="https://blog.schallbert.de/cnc-tuning-kinematics/"&gt;Optimierungsmöglichkeiten&lt;/a&gt;).&lt;/p&gt;&#10;&lt;p&gt;Zur Fertigung solcher Bilder muss die Maschine extrem oft die Z-Achse bewegen. Beim Fräsen von Punkten muss sie für jeden &amp;ldquo;Pixel&amp;rdquo; einmal zwischen Sicherheitshöhe und Materialeingriff wechseln.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Beispiel: Werden &lt;code&gt;400x400&lt;/code&gt; Punkte gefräst (Das wäre im Pixelmaßstab ein ziemlich mies aufgelöstes Bild), so muss die Maschine &lt;code&gt;32000x&lt;/code&gt; ihre Z-Achse in Gang setzen!&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Daher sollte die Sicherheitshöhe nur ein paar Zehntel über der Werkstückoberfläche liegen, z.B. &lt;code&gt;0.5mm&lt;/code&gt;. Um die schwere Y-Achse zu schonen, macht es Sinn, das Bild per &lt;em&gt;CAM&lt;/em&gt; zeilenweise auf der X-Achse abzuarbeiten. So wird die Y-Achse nur für Zeilensprünge bewegt.&lt;/p&gt;&#10;&lt;p&gt;Vorteilhaft ist hier eine leichte Portalfräsmaschine mit wenig Masse auf der Z-Achse. Sie sollte gut geschmiert und die Achsführungen gereinigt sein. Gleichzeitig sollte sie nicht zu steif sein, damit sie sich nicht durch die vielen abrupten Bewegungen aufschwingt.&lt;/p&gt;&#10;&lt;p&gt;Für Schrittmotoren und -Treiber sind diese Jobs Marathonrennen. Ihre Temperaturen sollten stets gut im Auge behalten werden. Ich empfehle, die Maschine nicht mit Halbtonbildern im Dauerbetrieb zu fahren. Die Achs- und Portalbelastungen hingegen sind vernachlässigbar: Die Maschine ist kaum Last in X- und Y-Richtung ausgesetzt, da sie zumeist nach unten im Materialeingriff steht und dies auch nur minimal zum Erzeugen kegelförmiger Vertiefungen.&lt;/p&gt;&#10;&lt;h3 id="labyrinthliniensegmente"&gt;Labyrinth/Liniensegmente&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-01-27-bloglogo_tsp.avif" alt="Image: TSP-paths my bloglogo connecting 9600 Vonoroi stipples" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Soll das gesamte Bild in nur einem Zug abgefahren werden, so muss erst ein &amp;ldquo;Problem des Handlungsreisenden&amp;rdquo; (&lt;a href="https://en.wikipedia.org/wiki/Travelling_salesman_problem" target="_blank" rel="noopener noreferrer" class="external-link"&gt;TSP&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) gelöst werden, wie es zum Beispiel die von &lt;a href="https://www.evilmadscientist.com/2012/stipplegen-weighted-voronoi-stippling-and-tsp-paths-in-processing/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;EvilMadScientist&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; vorgestellte Software &lt;em&gt;Stipplegen2&lt;/em&gt; vermag. Bei diesem Verfahren wird die Dichte der Liniensegmente pro Flächeneinheit erhöht, wenn ein Bereich besonders dunkel erscheinen soll. Auf der CNC-Maschine sind solche Bilder recht zügig fertiggestellt, da sie die Z-Achse nur einmal eintauchen lassen.&lt;/p&gt;&#10;&lt;p&gt;Nachteilig bei diesem Verfahren sind die geringe erreichbare Auflösung (Es werden sehr viele Punkte benötigt) und der geringe Kontrast, denn alle Linien sind gleich dick.&lt;/p&gt;&#10;&lt;h2 id="cnc-werkzeuge"&gt;CNC-Werkzeuge&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-01-27-graver.avif" alt="Image: This CNC graver is a good tool for halftone image fabrication" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Die Erzeugung von Halbtonbildern sind Gravurarbeiten. Daher empfiehlt sich die Verwendung von Gravierfräsern mit &lt;code&gt;60°&lt;/code&gt; oder &lt;code&gt;90°&lt;/code&gt; Spitzenwinkel sowie einer Abflachung von &lt;code&gt;0-0.5mm&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Ist das Motiv klein, so sollte gar keine Abflachung verwendet werden. Der Spitzenwinkel sollte groß sein, wenn nicht tief gefräst werden soll oder kann oder falls die &amp;ldquo;helle Farbe&amp;rdquo; die Kernfarbe des Materials ist. So werden tiefe Schatten vermieden, die das Bild zusätzlich abdunkeln.&lt;/p&gt;&#10;&lt;p&gt;Wird lackierte Aluminium-Verbundplatte bearbeitet, so kann ich Halbrundfräser mit &lt;code&gt;r&amp;lt;2mm&lt;/code&gt; bei sehr geringer Eintauchtiefe von maximal &lt;code&gt;0.18mm&lt;/code&gt; empfehlen. Hierbei wird allerdings vorausgesetzt, dass das Maschinenbett überplant und damit sehr eben ausgerichtet ist.&lt;/p&gt;&#10;&lt;h2 id="herstellung"&gt;Herstellung&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2025-01-27-bloglogo_tsp_machined.avif" alt="Image: TSP-paths on aluminium sandwich" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Bild: Versuch, mein Bloglogo als TSP-Pfad mit 9600 Kanten auf schwarz lackiertes Aluminiumverbundmaterial zu gravieren. Verwendet habe ich einen &lt;code&gt;1/8&amp;quot; 36°-Stichel mit 0.1mm Abflachung&lt;/code&gt;. Am unteren Bildrand ist zu erkennen, dass das Material etwas tiefer liegt und daher nicht mehr vom Stichel erreicht werden kann.&lt;/p&gt;&#10;&lt;h3 id="bildvorbereitung"&gt;Bildvorbereitung&lt;/h3&gt;&#10;&lt;p&gt;Das Bild sollte eine hohe Auflösung besitzen und in Graustufen vorliegen. Idealerweise besitzt es hohe Kontraste und nutzt das gesamte Grautonspektrum.&lt;/p&gt;&#10;&lt;p&gt;Nun wird das Bild entweder in ein CAM-Tool geladen, welche die Anfertigung von Halbtonbildern unterstützt - z.B. von &lt;a href="https://gadgets.vectric.com/v8/halftoner" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Vectric&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, Estlcam - oder aber in ein Bearbeitungswerkzeug, welches Punktewolken oder Pfade erzeugen kann, z.B. &lt;a href="https://www.evilmadscientist.com/2012/stipplegen2/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Stipplegen2&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; oder Inkscape.&lt;/p&gt;&#10;&lt;p&gt;Dort probiere ich stets ein paar verschiedene Einstellungen durch in Abhängigkeit vom verwendeten Material, dem gewünschten Fräser und der Bildwirkung. Oft sieht man bereits dort, ob das Foto wirken kann und erlaubt eine Einschätzung, ob die Auflösung für das verwendete Material geeignet ist.&lt;/p&gt;&#10;&lt;h3 id="wie-man-zu-guten-ergebnissen-kommt"&gt;Wie man zu guten Ergebnissen kommt&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Große Abmessungen bevorzugen: Je größer das Bild gewählt wird, desto weniger fallen Materialinhomogenitäten und ausgebrochene Bereiche auf.&lt;/li&gt;&#10;&lt;li&gt;Große Beobachtungsdistanz ermöglichen: Je weiter das Bild vom Betrachter entfernt positioniert wird, desto schärfer wird die Auflösung wahrgenommen. Bevor es dann irgendwann zu klein erscheint 😆&lt;/li&gt;&#10;&lt;li&gt;Lichteinfallwinkel berücksichtigen: Hat das Material Seidenglanz, metallischen Glanz oder ist sogar hochglänzend, so hängt die Abbildungsqualität sehr vom Blickwinkel und der Distanz zur Lichtquelle ab. Je glänzender das Material, desto kleiner wird der Bereich, in dem die Abbildung gut zu erkennen ist.&lt;/li&gt;&#10;&lt;li&gt;Homogene, nicht schmierende Materialien verwenden. Ich habe mit Acrylglas die besten Ergebnisse erzielen können.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="cnc-durchlauf"&gt;CNC-Durchlauf&lt;/h3&gt;&#10;&lt;p&gt;Hier ist vor allem eines mitzubringen: Zeit. Im Folgenden ein paar Videoausschnitte meiner Versuche (Zeitraffer).&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/n7CnjF5CUn1sidg6mxYhvL" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Graustufenbilder mit der CNC fertigen: Teil1&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Ausnahmen bestätigen die Regel. Natürlich gibt es sowohl bei Bildschirmen als auch bei Druckern Ausführungen, welche dieses Problem umgehen: Sie setzen etwa zusätzliche Subpixel mit zusätzlichen Farben ein und können so größere Farbräume abdecken oder - wie im Tiefdruck (Magazine, Verpackungen etc.) üblich - verwenden eine höhere Anzahl Farben beziehungsweise Farbmischungen, deren Tonwerte näher an den benötigten Halbtönen liegen.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Mehrfarbige Gravuren</title><link>https://blog.schallbert.de/engrave-multicolor/</link><pubDate>Mon, 30 Dec 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/engrave-multicolor/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-12-30-multicolorengrave-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: multicolored sign &amp;#39;&amp;#39;Helft Waldbraende verhueten&amp;#39;&amp;#39;"&#10; title="Mehrfarbige Gravuren" /&gt;&#10;&lt;h2 id="die-projektidee"&gt;Die Projektidee&lt;/h2&gt;&#10;&lt;p&gt;Vor ein paar Wochen war ich im Wald unterwegs. Dort sah ich ein wunderschönes, altes Schild mit dem Aufdruck &amp;ldquo;Helft Waldbrände verhüten&amp;rdquo;. Ein Zeichenstil wie in Tinte aus vergangenen Zeiten. Kurzerhand fotografierte ich es.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-12-30_waldbrand-original.jpg" alt="Image: An old *Waldbrände verhüten* sign" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Leider ist das Schild ziemlich verblichen und wirkt als Foto wenig plastisch. Daher beschloss ich, mit etwas Zeitaufwand aber ohne große Kosten mit Hilfe meiner CNC-Gravurmaschine eine dreidimensionale Kopie des Schildes anzufertigen.&lt;/p&gt;&#10;&lt;h2 id="vom-jpg-nach-svg"&gt;Vom &lt;code&gt;.jpg&lt;/code&gt; nach &lt;code&gt;.svg&lt;/code&gt;&lt;/h2&gt;&#10;&lt;p&gt;Bevor ich einen Job für die CNC erstellen kann, muss ich das Bild aufbereiten und in Vektorform bringen. Mein CAM-Programm kann mit Pixelgrafiken nämlich wenig anfangen. Ich möchte das Bild gern als &amp;ldquo;Carve&amp;rdquo; erstellen, also mit einem spitzen Gravierfräser diejenigen Bildbereiche schnitzen, die später farbig erscheinen sollen. Hierfür benötige ich zudem eine Farbtontrennung, da das Bild auch in der gefrästen Fassung später weiße, rote und schwarze Anteile haben soll.&lt;/p&gt;&#10;&lt;h3 id="bildaufbereitung"&gt;Bildaufbereitung&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-12-30_waldbrand-cleaned.jpg" alt="Image: Preparing the image for trace operation" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Hierfür nutze ich gängige Bildbearbeitungsprogramme wie &lt;a href="https://www.gimp.org/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;gimp&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und &lt;a href="https://www.getpaint.net/index.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;paint.net&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Ich verwende das &lt;em&gt;curves&lt;/em&gt;-Werkzeug, um den Kontrast zu erhöhen, richte die Perspektive mittels 3D-Drehung gerade und hole mir anschließend mit dem &lt;em&gt;Zuschneiden&lt;/em&gt;-Werkzeug den gewünschten Bildausschnitt. Anschließend radiere ich Bilddefekte, Schmutz oder durch die Kontrasterhöhung entstandene Artefakte weg. Zum Schluss noch ein wenig Schärfe hineindrehen und eine Tontrennung vornehmen, sodass ich keine Rottöne oder Graustufen, sondern reine Farben erhalte.&lt;/p&gt;&#10;&lt;p&gt;Nun exportiere ich das Bild als &lt;code&gt;.jpg&lt;/code&gt; und importiere es in &lt;a href="https://inkscape.org/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Inkscape&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 id="pfadkonversion"&gt;Pfadkonversion&lt;/h3&gt;&#10;&lt;p&gt;Hier skaliere ich das Bild zuerst, sodass es auf eine A4-Seite passt. Außerdem erstelle ich ganz unten links im Bild ein &lt;code&gt;1mm x 1mm&lt;/code&gt; kleines Quadrat, was mir für die beiden Farbschichtbilder später im CAM als Nullpunkt dienen soll, selbst aber nicht gefräst wird.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-12-30_waldbrand-trace.jpg" alt="Image: tracer in Inkscape" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Wie bereits in einem früheren &lt;a href="https://blog.schallbert.de/path-from-image/#inkscape"&gt;Artikel zur Pfadkonversion&lt;/a&gt; beschrieben, nutze ich die Trace-Funktion und kann so die roten und schwarzen Bereiche mittels unterschiedlicher Helligkeitsgrenzwerte in je eigene Layers freistellen. Die &amp;ldquo;rote&amp;rdquo; Layer enthält erst einmal auch die schwarzen Bildbereiche. Ich kann jedoch Layers voneinander subtrahieren, sodass nach dieser Aktion nur noch die roten Bildanteile übrig bleiben.&lt;/p&gt;&#10;&lt;p&gt;Anschließend lasse ich die Pfade noch vereinfachen und entferne fälschlicherweise erstellte Pfadartefakte. Ich exportiere die &amp;ldquo;rote&amp;rdquo; und die &amp;ldquo;schwarze&amp;rdquo; Layer jeweils als &lt;code&gt;.svg&lt;/code&gt;-Datei.&lt;/p&gt;&#10;&lt;h2 id="pfadplanung-im-cam"&gt;Pfadplanung im CAM&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-12-30-feuertrace.jpg" alt="Image: *red* areas to be cut" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Im CAM muss ich zwei Jobs planen - je einen für jede Farbe. Die unbearbeiteten Bildbereiche sollen weiß erscheinen. Die Gravur des ersten Jobs wird mit roter Farbe ausgelegt werden, und die zweite Gravur soll später tiefschwarz erscheinen.&lt;/p&gt;&#10;&lt;p&gt;Ich importiere also zuerst die &amp;ldquo;rote&amp;rdquo; SVG-Datei, wähle die für DIN A4 passende Skalierung, setze den Nullpunkt unten links und mache mich anschließend an die Erstellung der Carve-Pfade.&lt;/p&gt;&#10;&lt;h3 id="job1-rote-bereiche"&gt;Job1: &amp;ldquo;rote&amp;rdquo; Bereiche&lt;/h3&gt;&#10;&lt;p&gt;Ich setze eine Tiefenbegrenzung von &lt;code&gt;2mm&lt;/code&gt; und wähle für die Gravur einen &lt;code&gt;6mm 90°&lt;/code&gt; Stichel mit einer Schneide und &lt;code&gt;0.5mm&lt;/code&gt; Abflachung an der Spitze. Für die Räumarbeiten größerer gravierter Bereiche suche ich einen &lt;code&gt;3mm&lt;/code&gt; Zweischneider für Holz heraus und fräse mit &lt;code&gt;60%&lt;/code&gt; seitlicher Zustellung. Die Drehzahl stelle ich für beide Fräser bei &lt;code&gt;24000U/min&lt;/code&gt;, den Vorschub auf &lt;code&gt;3000mm/min&lt;/code&gt; ein.&lt;/p&gt;&#10;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-12-30-waldtrace.jpg" alt="Image: *black* areas to be cut" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="job2-schwarze-bereiche"&gt;Job2: &amp;ldquo;schwarze&amp;rdquo; Bereiche&lt;/h3&gt;&#10;&lt;p&gt;Bei den schwarzen Bereichen erlaube ich für Job2 eine höhere Tiefenzustellung von &lt;code&gt;2.8mm&lt;/code&gt; und spare mir den Räumfräser, da hier weniger zusammenhängende Flächen abgenommen werden müssen. Durch die größere Tiefe soll das Schwarz noch dunkler und das Motiv plastischer wirken.&lt;/p&gt;&#10;&lt;h2 id="durchführung"&gt;Durchführung&lt;/h2&gt;&#10;&lt;p&gt;Für das Projekt nehme ich eine schwarz durchgefärbte Faserplatte (MDF) zur Hand. Mit weißem Acryllack streiche ich sie mehrfach an, bis ich eine homogene und gut deckende Oberfläche habe. Nachdem alles getrocknet ist, bringe ich eine selbstklebende Folie auf. Profis nehmen Schablonenfolien (Maskierfolien) z.B. von Orafol, ich behelfe mir jedoch mit einer viel einfacheren Einschlagfolie für Bücher. Nicht perfekt, da ihre Haftung für kleine Flächen nicht ausreicht - aber gut genug für mich.&lt;/p&gt;&#10;&lt;h3 id="rote-flammen"&gt;Rote Flammen&lt;/h3&gt;&#10;&lt;p&gt;Nun lasse ich den ersten Job auf der Fräse durchlaufen. Dafür klebe ich das Werkstück mit Doppelklebeband im Arbeitsbereich der Fräse fest und richte sie ordentlich aus, sodass der Nullpunkt einfach gesetzt werden kann.&lt;/p&gt;&#10;&lt;p&gt;Sobald die Fräse durchgelaufen ist, setze ich Schrauben in die T-Nuten des Grundplatte an den Rändern des Werkstücks. So kann ich es nachher wiederholgenau positionieren. Anschließend löse ich es vom Maschinenbett, reinige es mit Bürste und Pinsel und bringe rote Sprühfarbe auf. Dummerweise zieht diese sofort in das MDF ein (siehe Video unten). Die Flammen bleiben somit schwarz. Ich probiere es erneut, wieder ohne Erfolg. Also muss ich nochmal ins Geschäft und einen viel viskoseren Lack zum Aufbringen per Pinsel kaufen.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Soll MDF lackiert werden, eignet sich Sprühfarbe wegen ihrer geringen Viskosität nicht: Sie zieht ein und hinterlässt kaum Pigmente auf der Oberfläche.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Nachdem ich auch hier wieder gewartet habe bis die ausgelegten Flammen getrocknet sind, montiere ich das Werkstück erneut auf dem Maschinenbett.&lt;/p&gt;&#10;&lt;h3 id="schwarze-tannen"&gt;Schwarze Tannen&lt;/h3&gt;&#10;&lt;p&gt;Nun lasse ich den zweiten Job durchlaufen. Dies geht trotz Verzicht auf den Räumfräser bedeutend schneller, da der Werkzeugwechsel entfällt.&lt;/p&gt;&#10;&lt;p&gt;Anschließend ziehe ich die Schutzfolie vom Werkstück ab und reinige es gründlich.&#10;Wie schön das Schild geworden ist!&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-12-30-multicolorengrave.jpg" alt="Image: carving complete: My copy of *Waldbrände verhüten*." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="video-der-3d-gravur"&gt;Video der 3D-Gravur&lt;/h2&gt;&#10;&lt;p&gt;Zu diesem Projekt gibt es mal wieder ein Zeitraffer-Video, bereitgestellt auf meinem PeerTube-Kanal.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/briT2e8qbbY6uWFFmv688n" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;multicolorEngrave&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="was-noch-besser-werden-könnte"&gt;Was noch besser werden könnte&lt;/h2&gt;&#10;&lt;p&gt;Im Nachhinein betrachtet gibt es bei meinen Projekten oft Dinge, die noch besser gemacht werden können. Wenn ich so auf das Video gucke, fallen mir zwei Sachen auf:&lt;/p&gt;&#10;&lt;h3 id="1-pfadqualität-bestimmt-fräsqualität"&gt;1. Pfadqualität bestimmt Fräsqualität&lt;/h3&gt;&#10;&lt;p&gt;Ich wollte möglichst schnell zum Ergebnis kommen und habe daher bei der Umwandlung vom Pixelbild zur Vektorgrafik nicht alle Pfadartefakte ausgeräumt. Diese äußern sich am fertigen Produkt als kleine Inseln und Punkte, die weniger tief gefräst wurden als der Rest. Am besten ist dies in den roten Bildbereichen zu erkennen. In Natura fällt das kaum auf. Im Video erkennt man aber, dass Fräse zum Ausarbeiten dieser Stellen zusätzliche Zeit benötigt.&lt;/p&gt;&#10;&lt;h3 id="2-cam-strategie-kostet-zeit"&gt;2. CAM-Strategie kostet Zeit&lt;/h3&gt;&#10;&lt;p&gt;Mein Programm für die Fräsplanung läuft Fräspfade für Räumung, Umrisse und Ausspitzungen von oben nach unten jeweils zeilenweise ab. Dies hat zur Konsequenz, dass unglaublich viele Positioniervorgänge für nicht zusammenhängende Pfade erforderlich werden. Zudem wird jeder Punkt im ungünstigsten Falle drei mal angefahren.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;G00&lt;/code&gt;-Kommandos sind hier schlecht, weil sie eine Menge Zeit kosten und die Maschine unnötige Wege verfährt. Dieses Problem ließe sich nur mit einem professionellerem CAM-Werkzeug beheben.&lt;/p&gt;&#10;</description></item><item><title>Repo-Lookout: Sicherheitslücke beheben</title><link>https://blog.schallbert.de/repo-lookout-fix-deploy/</link><pubDate>Fri, 20 Dec 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/repo-lookout-fix-deploy/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-12-20-repolookout-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Repolookout reporting a possible vulnerability on my site"&#10; title="Repo-Lookout: Sicherheitslücke beheben" /&gt;&#10;&lt;h2 id="repo-informationen-öffentlich-zugänglich"&gt;Repo-Informationen öffentlich zugänglich&lt;/h2&gt;&#10;&lt;p&gt;Eines schönen Tages bekam ich eine E-Mail von &lt;a href="https://www.repo-lookout.org/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Repo Lookout&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Darin stand, dass eines meiner Repositories für Zugriffe aus dem Internet freigegeben sei. Dies stelle ein potentielles Sicherheitsrisiko dar, da möglicherweise geheime Quelldateien, versteckte Funktionen oder sogar Passwörter enthalten sein könnten.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-12-20_repolookout_warning.jpg" alt="Image: RepoLookout mail notification" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Zuerst hielt ich diese &amp;ldquo;Ihr-Repo-ist-nicht-sicher&amp;rdquo;-Warnung für einen Phishing-Versuch.&#10;Doch durch einfaches Eingeben der darin enthaltenen Links wurde klar, dass &lt;em&gt;Repo Lookout&lt;/em&gt; Recht hatte und mein &lt;a href="https://lectures.schallbert.de" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Lectures&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;-Repo nicht nur wie von mir gewünscht öffentlich war, sondern dass auch die Metadaten zur Versionskontrolle offen auf dem Webserver lagen.&lt;/p&gt;&#10;&lt;h3 id="ist-das-schlimm"&gt;Ist das schlimm?&lt;/h3&gt;&#10;&lt;p&gt;Normalerweise sollen innere Struktur und Konfiguration (Actions, Diskussionen, Wiki etc.) hinter einem Repository nicht öffentlich bleiben. Erst recht nicht, wenn das Repository als &lt;code&gt;private&lt;/code&gt; angelegt ist. Doch auch bei öffentlichen Repos sollte niemand auf die Struktur dahinter zugreifen können.&lt;/p&gt;&#10;&lt;p&gt;Daher die Mission von Repo-Lookout:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Find source code repositories that have been inadvertently exposed to the public and report them to the domain&amp;rsquo;s technical contact.&amp;rdquo; - Repo Lookout /about (&lt;a href="https://www.crissyfield.de/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Crissy Field GmbH&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;In diesem Falle unkritisch, aber unerwartet und unschön.&lt;/p&gt;&#10;&lt;p&gt;Schadet mir nicht, weil mein Webserver die Dateien lediglich zum Abruf dort liegen hat und sie selbst bei Manipulation keine Rückwirkung auf mein Repository gehabt hätten. Außerdem hatte ich alle Secrets wie &lt;a href="https://blog.schallbert.de/server-config-version-control/#secrets-in-docker-composeyml"&gt;in diesem Artikel beschrieben&lt;/a&gt; in eigens angelegte Dateien ausgelagert, sodass sie nicht mehr in den Konfigurationsdateien auftauchen. Das Repository liegt zudem getrennt auf der &lt;a href="https://git.schallbert.de/schallbert/lectures" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Gitea-Server-Instanz&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Dennoch sollte nur am Internet hängen, was ich bewusst freigeben möchte.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-12-20_repo_commithistory.jpg" alt="Image: The repository&amp;#39;s commit history is public anyways" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Der Bildausschnitt von &lt;em&gt;Gitea&lt;/em&gt; zeigt denselben Commit wie die Warnung von &lt;em&gt;Repo Lookout&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h3 id="wie-es-dazu-kam"&gt;Wie es dazu kam&lt;/h3&gt;&#10;&lt;p&gt;In der Deploy-Pipeline für meine Subdomain &lt;code&gt;lectures.schallbert.de&lt;/code&gt; und Landing Page &lt;code&gt;schallbert.de&lt;/code&gt; habe ich einen direkt-Checkout von der &lt;em&gt;Gitea&lt;/em&gt;-Instanz zum Webserver &lt;em&gt;Caddy&lt;/em&gt;. Dieser läuft automatisch an, sobald die &lt;code&gt;main&lt;/code&gt;-Branches ein Update erhalten. Der Runner startet eine &lt;a href="https://github.com/marketplace/actions/checkout" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Checkout-Action&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, welche das Repository ins entsprechende Verzeichnis des Webservers kopiert.&lt;/p&gt;&#10;&lt;h3 id="checkout-action-kopiert-auch-den-git-ordner"&gt;Checkout-Action kopiert auch den &lt;code&gt;.git&lt;/code&gt;-Ordner&lt;/h3&gt;&#10;&lt;p&gt;Dabei wird der &lt;code&gt;.git&lt;/code&gt;-Ordner einfach mit aufgesetzt. Dort drin liegen alle von der Versionskontrollsoftware &lt;a href="https://git-scm.com/docs/gitrepository-layout" target="_blank" rel="noopener noreferrer" class="external-link"&gt;benötigten Daten zur Zustandsverwaltung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; des Repos. Den Kopiervorgang selbst kann ich auf &lt;em&gt;Gitea&lt;/em&gt; nicht sehen, denn dort taucht der versteckte &lt;code&gt;.git&lt;/code&gt;-Ordner gar nicht erst im Verzeichnis auf. Verständlich, denn auf Basis dieses Ordners findet die gesamte Darstellung auf &lt;em&gt;Gitea&lt;/em&gt; statt.&lt;/p&gt;&#10;&lt;p&gt;So blieb das unerwünschte Verhalten unter meinem Radar - und laut &lt;em&gt;Repo Lookout&lt;/em&gt; bin ich bei Weitem nicht der Einzige, dem es so geht.&lt;/p&gt;&#10;&lt;h2 id="option-1-fix-auf-dem-webserver"&gt;Option 1: Fix auf dem Webserver&lt;/h2&gt;&#10;&lt;p&gt;Die naheliegendste Lösung ist, den Zugriff auf die Datei serverseitig zu blockieren. Dies kostet nur wenige Ressourcen und ist leicht einzurichten.&lt;/p&gt;&#10;&lt;p&gt;Dieser Foreneintrag zeigt, wie das geht: &lt;a href="https://caddy.community/t/v2-hide-entire-folder-caddyfile/7234" target="_blank" rel="noopener noreferrer" class="external-link"&gt;hide-entire-folder-caddyfile&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Für meine zu schützenden Dateien füge ich also folgende Einträge der &lt;code&gt;Caddyfile&lt;/code&gt; hinzu:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /caddy2/Caddyfile&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;respond /.git/* &amp;#34;Access denied&amp;#34; 403&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;respond /.gitea/* &amp;#34;Access denied&amp;#34; 403&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Damit sage ich Caddy, dass es bei Aufruf einer beliebigen Datei &lt;code&gt;/*&lt;/code&gt; im Ordner &lt;code&gt;/.git&lt;/code&gt; mit dem Fehlercode &lt;code&gt;403&lt;/code&gt; &amp;ldquo;Forbidden&amp;rdquo; antworten soll. Dabei ist die Wildcard (&lt;code&gt;*&lt;/code&gt;) unbedingt erforderlich, denn sonst wird &lt;em&gt;lediglich der Ordner selbst&lt;/em&gt; und nicht die enthaltenen Dateien gesperrt.&lt;/p&gt;&#10;&lt;p&gt;Zur Kontrolle prüfe ich was passiert, wenn ich die Git Logs anfrage:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;# Terminal&#10;curl &amp;lt;lectures.schallbert.de&amp;gt;/.git/logs/HEAD&#10;Access denied &#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Funktioniert prima!&lt;/p&gt;&#10;&lt;h2 id="option-2-fix-in-der-checkout-action"&gt;Option 2: Fix in der Checkout-Action&lt;/h2&gt;&#10;&lt;p&gt;Es gibt allerdings eine noch viel elegantere Lösung: bereits vorher in der Pipeline dafür sorgen, dass der Ordner gar nicht erst auf dem Server auftaucht.&lt;/p&gt;&#10;&lt;h3 id="variante-1-mit-hilfe-von-sparse-checkout"&gt;Variante 1: Mit Hilfe von &lt;code&gt;sparse-checkout&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/33933702/git-checkout-except-one-folder" target="_blank" rel="noopener noreferrer" class="external-link"&gt;sparse-checkout&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ermöglicht die Auswahl von Ordnern und Dateien, die zum Checkout gehören sollen. Alle anderen Dateien im Repository bleiben unberührt und tauchen im Branch nicht auf. Dies spart vor allem bei großen Repositories Zeit und Speicherplatz. Macht aber natürlich nur Sinn wenn bereits vorher bekannt ist, dass nicht alle Dateien angefasst werden müssen.&lt;/p&gt;&#10;&lt;h3 id="negativliste-für-sparse-checkout"&gt;Negativliste für &lt;code&gt;sparse-checkout&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;In meinem Falle will ich die oben erwähnten Ordner gerade &lt;em&gt;nicht&lt;/em&gt; per Checkout auf den Server kopieren, den ganzen Rest aber schon. Wie ich das hinbekomme? Mit Hilfe der Negation im &lt;code&gt;no-cone&lt;/code&gt; mode.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;The user has explicitly said &amp;lsquo;I want these directories and not those directories.&amp;rsquo;&amp;rdquo; - Derrick Stolee, Microsoft, auf &lt;a href="https://github.com/git/git/commit/55dfcf9591b088ce60ec80eb5425dda18223cac0" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Github&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;In der &lt;a href="https://github.com/marketplace/actions/checkout" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Anleitung zu Checkout-Action&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; steht, dass &lt;code&gt;sparse-checkout&lt;/code&gt; auch für die vom Runner automatisierte Aktion unterstützt wird.&lt;/p&gt;&#10;&lt;p&gt;Nun programmiere ich mit Hilfe des &lt;a href="https://github.github.com/actions-cheat-sheet/actions-cheat-sheet.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Github Actions Cheet Sheet&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /.gitea/workflows/deploy-lectures.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;CHECKOUT ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v4&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;path&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;./tmp&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;sparse-checkout&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; /*&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; !.git&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; !.gitea&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;sparse-checkout-cone-mode&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Zur Erläuterung: Das Skript zum Veröffentlichen auf meinem Webserver verwendet die Aktion &lt;code&gt;checkout&lt;/code&gt;, Unterfunktion &lt;code&gt;sparse-checkout&lt;/code&gt; und bezieht sämtliche Dateien im Ordner im Stammverzeichnis &lt;code&gt;tmp&lt;/code&gt; und darunter ein bis auf &lt;code&gt;.git&lt;/code&gt; und &lt;code&gt;.gitea&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="was-ist-der-no-cone-mode"&gt;Was ist der &lt;code&gt;No-Cone Mode&lt;/code&gt;?&lt;/h3&gt;&#10;&lt;p&gt;Standardmäßig erwartet &lt;code&gt;sparse-checkout&lt;/code&gt; eine Liste von Ordnern, die für den Checkout erfasst werden sollen. Im &lt;code&gt;no-cone&lt;/code&gt; Modus wird stattdessen eine Liste von Patterns erwartet. Hier sind sämtliche Operatoren möglich, die auch in der &lt;code&gt;.gitignore&lt;/code&gt; zum Spezifizieren von Dateien, Ordnern, Auslassungen etc. verwendet werden können. Dies ermöglicht mir den Ausschluss bestimmter Ordner, hat aber einige &lt;a href="https://git-scm.com/docs/git-sparse-checkout#_internalsnon_cone_problems" target="_blank" rel="noopener noreferrer" class="external-link"&gt;gewichige Nachteile&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Durch viel höhere Komplexität der Pattern-Befehle, die damit einhergehende Fehleranfälligkeit sowie die deutlich rechenintensivere Auswertung bei größeren Repositories wird die Verwendung des &lt;code&gt;no-cone&lt;/code&gt; Mode nicht empfohlen und ist in der Dokumentation als &amp;ldquo;deprecated&amp;rdquo; (überholt) angegeben. Dennoch, probieren geht über Studieren!&lt;/p&gt;&#10;&lt;h3 id="test-mit-sparse-checkout"&gt;Test mit &lt;code&gt;sparse-checkout&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;Nun lade ich die Action auf meine Gitea-Instanz hoch und lass meinen &lt;em&gt;runner&lt;/em&gt; einmal drüber laufen. Anschließend logge ich mich auf dem Webserver ein und schaue, ob der &lt;code&gt;.git&lt;/code&gt;-Ordner erstellt wurde oder nicht:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;lectures# ls -la&#10;[...]&#10;drwxr-xr-x 8 root root 4096 Dec 20 10:41 .git&#10;[...]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Mist, der Ordner ist ja immer noch da. Ich schaue in den Logs der Action auf meiner Gitea-Instanz nach:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;[...]&#10;hint: &#9;git branch -m &amp;lt;name&amp;gt;&#10;Initialized empty Git repository in /workspace/schallbert/lectures/tmp/.git/&#10;[...]&#10;::group::Setting up sparse checkout&#10;[command]/usr/bin/git config core.sparseCheckout true&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;An &lt;code&gt;sparse-checkout&lt;/code&gt; liegt es also nicht. Dafür aber an der Art und Weise, wie &lt;em&gt;checkout&lt;/em&gt; funktioniert: Offensichtlich wird für ein ordentliches Aufsetzen des Repository auf meinem Webserver der &lt;code&gt;.git&lt;/code&gt;-Ordner zwingend benötigt. Also bleibt mir nur übrig, ihn nach dem Auschecken automatisch zu löschen.&lt;/p&gt;&#10;&lt;h3 id="variante-2-rm--rf"&gt;Variante 2: &lt;code&gt;rm -rf&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;Und so probiere ich es mit Gewalt:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /.gitea/workflows/deploy-lectures.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;CHECKOUT ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v4&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;path&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;./tmp&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;REMOVE TEMPORARY FILES ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; rm -rfv ./tmp/.git ./tmp/.gitea&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nun endlich taucht der &lt;code&gt;.git&lt;/code&gt;-Ordner auf meinem Webserver nicht mehr auf und mein &amp;ldquo;Repo-Leak&amp;rdquo; ist geflickt. Nochmal danke an &lt;em&gt;Repo Lookout&lt;/em&gt;!&lt;/p&gt;&#10;&lt;h2 id="fazit"&gt;Fazit&lt;/h2&gt;&#10;&lt;p&gt;Ich hatte hier das Problem, dass der versteckte &lt;code&gt;.git&lt;/code&gt;-Ordner, wo Konfiguration und Struktur von Repositories gespeichert werden, unbeabsichtigt und ohne mein Wissen auf meinem Webserver veröffentlicht waren.&lt;/p&gt;&#10;&lt;p&gt;Ich habe hier zwei funktionierende Optionen zur Lösung des Problems vorgestellt:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Ein Zugriffsverbot auf dem Webserver einrichten&lt;/li&gt;&#10;&lt;li&gt;Die Pipeline so umbauen, dass sie den &lt;code&gt;.git&lt;/code&gt;-Ordner nach erfolgtem Ausrollen selbstständig löscht.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Die zweite Option ist zwar etwas aufwendiger einzubauen, doch packt sie das Problem bei der Wurzel, anstatt lediglich die Symptome zu beheben. Außerdem entspricht sie dem ersten Grundsatz aus dem Datenschutz: Datenminimierung geht vor Schutzmaßnahmen.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Was nicht existiert, kann nicht verloren gehen&amp;rdquo; - Schallbert&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;</description></item><item><title>Logrotate-Rekursion</title><link>https://blog.schallbert.de/logrotate-mistake/</link><pubDate>Mon, 18 Nov 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/logrotate-mistake/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-11-18_logrotate-recursion.jpg"&#10; class="post-cover"&#10; alt="Image: Logrotate creating .1 recursively"&#10; title="Logrotate-Rekursion" /&gt;&#10;&lt;h2 id="was-funktioniert-nicht"&gt;Was funktioniert nicht?&lt;/h2&gt;&#10;&lt;p&gt;Eigentlich wollte ich &lt;a href="https://github.com/logrotate/logrotate" target="_blank" rel="noopener noreferrer" class="external-link"&gt;logrotate&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; nutzen, um meine Logs täglich zu bereinigen und so Datensparsamkeit umzusetzen: So können IP-Adressen, die ich für&amp;rsquo;s &lt;a href="https://blog.schallbert.de/server-protection/"&gt;Banning&lt;/a&gt; brauche, einheitlich und automatisiert nach kurzer Zeit gelöscht werden. Die Server-Logs interessieren mich ansonsten überhaupt nicht und stehlen mir nur Speicherplatz.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-11-18_logrotate-recursion.jpg" alt="Image: The way logrotate does the rotation looks all too recursive." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Mit meiner aktuellen Konfiguration erstellt &lt;code&gt;logrotate&lt;/code&gt; zwar eine Logdatei am Tag, nennt sie aber &lt;code&gt;xyz.log.1, xyz.log.1.1, xyz.log.1.1.1&lt;/code&gt; anstatt der erwarteten &lt;code&gt;xyz.log.1, xyz.log.2, xyz.log.3&lt;/code&gt;. Somit funktioniert die Löschroutine nicht mehr und die Anzahl der Logs wächst stetig.&lt;/p&gt;&#10;&lt;h2 id="und-warum"&gt;Und warum?&lt;/h2&gt;&#10;&lt;p&gt;Ich vermute, dass meine &lt;em&gt;logrotate&lt;/em&gt;-Konfiguration fehlerhaft ist. Im Moment sieht sie wie folgt aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## LOGROTATE file named &amp;#39;blog&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;blog/* {&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# it&amp;#39;s ok if the file doesn&amp;#39;t already exist&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;missingok&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Sets the logs to rotate in intervals&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;daily&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Tells the system to remove old logs and only keep the most recent rotated logs&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;rotate 7&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Rotated logs will be compressed and kept on disk if they are 10 MB or less.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;size 10M&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# compress and delaycompress: These two options are used together and indicate that &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# rotated logs should be compressed (gzip) except for the most recent one.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;compress&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;delaycompress&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Wie üblich nehme ich als Erstes die Bedienungsanleitung zur Hilfe, die ich mittels &lt;code&gt;man logrotate&lt;/code&gt; in der Konsole aufrufe.&#10;Dort steht, dass die zu rotierende Datei direkt angegeben (und nicht nur per &amp;ldquo;Wildcard&amp;rdquo; gewählt &lt;code&gt;*&lt;/code&gt;) werden kann. Außerdem kann ein Logverhalten auch auf mehrere Dateipfade angewendet werden, indem sie durch Leerzeichen getrennt vor die geschweifte Klammer gesetzt werden.&lt;/p&gt;&#10;&lt;p&gt;Das Sternchen scheint genau mein Problem mit den kaskadierenden Logs zu erzeugen: Dadurch wird nicht nur die eigentliche Zieldatei &lt;code&gt;access.log&lt;/code&gt; rotiert sondern auch alle bereits rotierten Dateien nochmal angefasst.&lt;/p&gt;&#10;&lt;h2 id="die-lösung"&gt;Die Lösung&lt;/h2&gt;&#10;&lt;p&gt;Entsprechend der Bedienungsanleitung schreibe ich die Bearbeitungsvorschrift für Logrotate so um, dass ich statt fünf eigenen Vorschriften für die jeweiligen Logs nur zwei Dateien erzeuge. Diese sind zudem einfacher und kürzer als die obige Ausgangsversion.&lt;/p&gt;&#10;&lt;p&gt;Beispiel:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# rotate webserver log files.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;gitea/access.log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;blog/access.log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;landing/access.log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;lectures/access.log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Sets the logs to rotate in intervals&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;daily&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Tells the system to remove old logs and only keep the most recent rotated logs&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;rotate 7&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# compress and delaycompress: These two options are used together and indicate that &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# rotated logs should be compressed (gzip) except for the most recent one.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;compress&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;delaycompress&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="konfigurations-update"&gt;Konfigurations-Update&lt;/h3&gt;&#10;&lt;p&gt;Dies kann ich durch meine vorhandene Server-Konfigurations-Pipeline durch einfachen Push ins Repository auf dem Server ausrollen.&#10;Nun muss ich die Dateien dort nur noch händisch nach &lt;code&gt;/etc/logrotate.d&lt;/code&gt; kopieren.&lt;/p&gt;&#10;&lt;h3 id="logrotate-testen"&gt;Logrotate testen&lt;/h3&gt;&#10;&lt;p&gt;Jetzt wäre es noch schön, wenn ich ausprobieren könnte, ob die von mir vorgenommenen Änderungen auch tatsächlich funktionieren. Dafür nehme ich erneut die Bedienungsanleitung von &lt;em&gt;logrotate&lt;/em&gt; zur Hilfe und tippe:&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;logrotate -d -v &amp;lt;LOGROTATE-DESCRIPTOR-FILE&amp;gt;&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Mit aktivierten &lt;em&gt;debug&lt;/em&gt; und &lt;em&gt;verbose&lt;/em&gt; Flags wird logrotate die in der Konfigurationsdatei angegebene Rotationsvorschrift anwenden und Rückmeldung geben, tatsächlich auf dem Dateisystem aber keine Änderungen oder Rotationen durchführen. Bei mir sieht das Ergebnis des Tests dann etwa so aus:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;lectures/access.log &#10; after 1 days (7 rotations)&#10;empty log files are rotated, old logs are removed&#10;considering log gitea/access.log&#10; Now: 2024-11-28 17:19&#10; Last rotated at 2024-11-27 00:00&#10; log needs rotating&#10;considering log blog/access.log&#10; Now: 2024-11-28 17:19&#10; Last rotated at 2024-11-16 00:00&#10; log needs rotating&#10;considering log landing/access.log&#10; Now: 2024-11-28 17:19&#10; Last rotated at 2024-11-07 00:00&#10; log needs rotating&#10;considering log lectures/access.log&#10; Now: 2024-11-28 17:19&#10; Last rotated at 2024-09-21 00:00&#10; log needs rotating&#10;rotating log gitea/access.log, log-&amp;gt;rotateCount is 7&#10;[...]&#10;renaming lectures/access.log.7.gz to lectures/access.log.8.gz (rotatecount 7, logstart 1, i 7), &#10;renaming lectures/access.log.6.gz to lectures/access.log.7.gz (rotatecount 7, logstart 1, i 6), &#10;renaming lectures/access.log.5.gz to lectures/access.log.6.gz (rotatecount 7, logstart 1, i 5), &#10;renaming lectures/access.log.4.gz to lectures/access.log.5.gz (rotatecount 7, logstart 1, i 4), &#10;renaming lectures/access.log.3.gz to lectures/access.log.4.gz (rotatecount 7, logstart 1, i 3), &#10;renaming lectures/access.log.2.gz to lectures/access.log.3.gz (rotatecount 7, logstart 1, i 2), &#10;renaming lectures/access.log.1.gz to lectures/access.log.2.gz (rotatecount 7, logstart 1, i 1), &#10;renaming lectures/access.log.0.gz to lectures/access.log.1.gz (rotatecount 7, logstart 1, i 0)&#10;[...]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Die Ausgabe zeigt mir, dass sowohl alle gewünschten Logdateien von &lt;em&gt;logrotate&lt;/em&gt; berücksichtigt werden und auch, dass die Rotation korrekt und täglich durchgeführt wird.&lt;/p&gt;&#10;&lt;p&gt;Wieder etwas gelernt 😃&lt;/p&gt;&#10;</description></item><item><title>Klingenbutler</title><link>https://blog.schallbert.de/klingenbutler/</link><pubDate>Mon, 14 Oct 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/klingenbutler/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-10-14_klingenbutler-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: First prototype of Klingenbutler"&#10; title="Klingenbutler" /&gt;&#10;&lt;h2 id="warum-klingenbutler"&gt;Warum Klingenbutler?&lt;/h2&gt;&#10;&lt;p&gt;Nachdem das Problem der fehlenden Ablage für feste Shampoos in meiner Dusche gelöst hatte - daraus entstand der &lt;a href="https://blog.schallbert.de/seifenbutler-pilot-run/"&gt;Seifenbutler&lt;/a&gt; - fielen mir dort herumfliegende Rasierer mehr und mehr störend auf.&lt;/p&gt;&#10;&lt;p&gt;Liegen sie unten auf dem Rand der Dusche, bildet sich um sie herum eine Lache. Dort entstehen dann besonders viele Kalkflecken und die Reinigung geht schlechter von der Hand. Legt man die auf die Armaturen, fallen sie früher oder später herunter und werden beschädigt. Selbiges gilt für die wackelige Ablage auf dem Rand der Duschkabine.&lt;/p&gt;&#10;&lt;p&gt;Es liegt nahe, die Klemmmechanik des Seifenbutler wiederzuverwenden. Somit muss nur der &amp;ldquo;vordere Teil&amp;rdquo; neu designt werden und schon erhalte ich eine formschöne Ablage für Rasierer - den &lt;em&gt;Klingenbutler&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h2 id="das-design"&gt;Das Design&lt;/h2&gt;&#10;&lt;p&gt;Ich mag die runde Formgebung des Seifenbutler und möchte den Klingenbutler ähnlich gestalten. Die Rasierer sollen allerdings nicht allzu leicht versehentlich berührt werden können. Daher entschließe ich mich, sie möglichst &amp;ldquo;innen&amp;rdquo; anzubringen. Hierfür erstelle ich einen Ring mit Öffnung, der wiederum drei Einkerbungen für Rasierer besitzt. Die Radien der Einkerbungen gestalte ich so, dass alle mir zur Verfügung stehenden Nassrasierer dort hineinpassen und sogar Zahnbürsten sich ohne Kontakt der Borsten zum Klingenbutler einhängen lassen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-10-14_klingenbutlercad.jpg" alt="Image: CAD drawing of Klingenbutler V1.0" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die Öffnung (in der Zeichnung links) gestalte ich für Ober- und Unterschale so, dass die Oberschale in allen möglichen geschlossenen Positionen stets die Unterschale überdeckt. Damit vermeide ich überstehende Kanten bei besonders großen oder kleinen Duschstangendurchmessern.&lt;/p&gt;&#10;&lt;h3 id="einrasten-der-rasierer"&gt;Einrasten der Rasierer&lt;/h3&gt;&#10;&lt;p&gt;Schwieriger war schon die Planung von Rasthilfen, sodass die Rasierer sich nicht von allein lösen können. Hier entschied ich mich beim ersten Entwurf für eine Kerbe in Form eines &lt;a href="https://en.wikipedia.org/wiki/Tetrahedron" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Doppeltetraeders&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Im Bild oben ist die Kerbe mit &amp;ldquo;drain&amp;rdquo; beschriftet. Sie besitzt auf ca. &lt;code&gt;15mm&lt;/code&gt; Länge ein Gefälle von &lt;code&gt;3mm&lt;/code&gt;, lässt Spritzwasser ablaufen und ermöglicht eine schnelle Trocknung der Rasierer.&lt;/p&gt;&#10;&lt;h3 id="g2-kurven"&gt;G2-Kurven&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-10-14_twodifferentradii.jpg" alt="Image: two different radii joined. There is a visible kink due to differences in slope at that point." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Als ich einen meiner Entwürfe kurz vor Einstieg in die Fertigung einem Bekannten von mir zeigte - er ist 3D-Designer von Beruf - fiel ihm sofort auf, dass ich keinen fließenden Übergang zweier Kurvenradien vorgesehen hatte. Er meinte, dass in solchen Fällen &amp;ldquo;G2-Kurven&amp;rdquo; im CAD eingesetzt werden und das Modell so viel harmonischere Übergänge bekäme.&lt;/p&gt;&#10;&lt;p&gt;Im Fachjargon scheint man dabei von &lt;a href="https://en.wikipedia.org/wiki/Smoothness#Geometric_continuity" target="_blank" rel="noopener noreferrer" class="external-link"&gt;geometrischer Stetigkeit&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zu sprechen. Sogleich fragte ich die Softwarefirma hinter meinem CAD-Werkzeug, ob sie &lt;a href="https://hobbyline.info/forum/index.php?thread/822-wie-mache-ich/&amp;amp;postID=11565#post11565" target="_blank" rel="noopener noreferrer" class="external-link"&gt;so etwas ebenfalls unterstütze&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und siehe da - es gibt das Werkzeug &lt;em&gt;tangentenstetige Verbindung erzeugen&lt;/em&gt;. Von nun an werde ich dieses Werkzeug verwenden, wenn ich Radien miteinander verbinde.&lt;/p&gt;&#10;&lt;p&gt;Das Werkzeug erzeugt eine &lt;a href="https://en.wikipedia.org/wiki/B%C3%A9zier_curve" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Bezier-Kurve&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, deren Endpunkte tangential an die beiden Kreissegmente anschließen und die Knickstelle auf diese Weise beseitigen.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-10-14_twodifferentradiig2.jpg" alt="Image: radii joined with tangential continuity using a Bezier Curve" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Diese Herangehensweise hat außer der gefälligeren Optik sogar noch einen Vorteil in der Fertigung: Beim Abfahren von Knickstellen muss die CNC-Maschine plötzliche Richtungsänderungen durchführen, ähnlich wie bei verbundenen Liniensegmenten, die in einem Winkel aufeinandertreffen. Somit muss abgebremst und im neuen Kurvenradius beschleunigt werden. Durch Verwendung tangentenstetiger Verbindungen hingegen werden Beschleunigungsspitzen deutlich abgemildert und die Maschine läuft mit gleichmäßigerer Geschwindigkeit durch.&lt;/p&gt;&#10;&lt;p&gt;Hat man wie mit dem &lt;em&gt;Klingenbutler&lt;/em&gt; Projekte mit vielen &amp;ldquo;Knickstellen&amp;rdquo; in der Zeichnung, so kommen hier schnell ein paar Sekunden Fertigungszeit pro Teil zusammen, die leicht einzusparen sind - dazu vielleicht später mal mehr.&lt;/p&gt;&#10;&lt;h3 id="erster-prototyp---version-10"&gt;Erster Prototyp - Version 1.0&lt;/h3&gt;&#10;&lt;p&gt;Die Fertigung trat ich mit meinem &amp;ldquo;klassischen&amp;rdquo; Prototypenmaterial an: gegossenes PMMA (Acrylglas) mit &lt;code&gt;5mm&lt;/code&gt; Stärke in opak weiß. Verwendet man einigermaßen passende Parameter, so verhält sich das Material äußerst gutmütig. Gleichzeitig legt das Material schonungslos alle Fehler offen: Hier eine Fase etwas zu weit, dort leichte Rattermarken durch Herausbeschleunigen der Maschine aus einer Ecke, im Boden des Gewindes gefangene Späne - All das tritt die etwas lichtdurchlässige Materialkomposition und starke Reflexionen an Materialkanten stark hervor.&#10;Daher ist dieses Material hervorragend geeignet, Design- und Fertigung von Produkten zu verbessern.&lt;/p&gt;&#10;&lt;p&gt;Durch meine Erfahrungen mit dem Seifenbutler gingen Niederhalten während der Fertigung und Zusammenbau des Klingenbutler problemlos vonstatten. Zahnbürsten und Rasierer sind von nun an an einem festen Platz in der Dusche zu finden 🙂.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-10-14_firstproto.jpg" alt="Image: First prototype of Klingenbutler V1.0" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="version-11---fertigung-in-bambus"&gt;Version 1.1 - Fertigung in Bambus&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-10-14_bambus.jpg" alt="Image: First time Bamboo on my CNC" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Eigentlich will ich ja weg von Kunststoff. Die Produktion meiner Seifenbutler habe ich im Laufe des Jahres zwar bereits auf &amp;ldquo;greencast&amp;rdquo; Acrylglas umgestellt - also PMMA, das selbst aus 100% recycelter Ware besteht - aber besser wäre ja noch, gar keinen Kunststoff einzusetzen.&lt;/p&gt;&#10;&lt;p&gt;Dafür ließ ich mir ein paar Muster &amp;ldquo;Moso-Bambus&amp;rdquo; kommen: Stäbchen-Leimholz in drei Schichten kreuzverleimt, ähnlich wie Sperrholz, knapp &lt;code&gt;7.5mm&lt;/code&gt; stark. Für die Fertigung in Bambus legte ich die Passtoleranzen etwas großzügiger aus (&lt;code&gt;0.1mm&lt;/code&gt; Untermaß )und wandelte metrische Gewinde im Plan schlicht in einfache Bohrungen für Holzschrauben um.&lt;/p&gt;&#10;&lt;h3 id="unnötiges-weglassen"&gt;Unnötiges weglassen&lt;/h3&gt;&#10;&lt;p&gt;Beim Zusammenbau des ersten Prototypen wurde mir klar, dass das dritte Bajonett überflüssig geworden war. Zusammen mit ein paar kleineren, weiteren Änderungen entwickelte ich Version 1.1:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Dritte Verschraubung im Rastkreis weglassen&lt;/li&gt;&#10;&lt;li&gt;Rastnasen/Ablaufrinne: Durchgängige Gravur ohne Stoppen bei &amp;ldquo;Schlüsselloch&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;Bajonett: Innenseiten auf Rückseite anfasen und so die Oberfläche durchbrechen&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="drehzahl-und-vorschub"&gt;Drehzahl und Vorschub&lt;/h3&gt;&#10;&lt;p&gt;Für das Vollnutfräsen nutze ich einen DLC-beschichteten (Diamond-like-carbon) &lt;code&gt;4mm&lt;/code&gt; Zweizahnfräser ohne Drall mit &lt;code&gt;12mm&lt;/code&gt; Schneidenlänge. Ich betreibe ihn bei &lt;code&gt;S30000U/min&lt;/code&gt; mit einem Vorschub von&lt;code&gt;F3000mm/min&lt;/code&gt;. Ich gehe direkt in die volle Materialtiefe mit &lt;code&gt;Z+ 7.5mm&lt;/code&gt; und fahre Bohrungen und Materialrampen mit &lt;code&gt;20°&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Fasen bringe ich mittels eines &lt;code&gt;90°&lt;/code&gt; Gravurfräser, dreischneidig, Durchmesser &lt;code&gt;10mm&lt;/code&gt; an. Ihn betreibe ich mit &lt;code&gt;F5000mm/min&lt;/code&gt; bei &lt;code&gt;S15000U/min&lt;/code&gt; und einem Mittenabstand von &lt;code&gt;2mm&lt;/code&gt;, was bei einer glatten Kante mit &lt;code&gt;0.5mm&lt;/code&gt; Breite schon bedeutet, dass er &lt;code&gt;2.5mm&lt;/code&gt; Luft &amp;ldquo;nach unten&amp;rdquo; in Z-Richtung benötigt.&lt;/p&gt;&#10;&lt;p&gt;Für Bohrungen, Taschen und kleine Aussparungen verwende ich einen hinterschliffenen &lt;code&gt;2mm&lt;/code&gt; Zweischneider Fischschwanz mit &lt;code&gt;15°&lt;/code&gt; Drall und &lt;code&gt;20mm&lt;/code&gt; Schneidenlänge. Ihn betreibe ich mit vorsichtigen &lt;code&gt;1700mm/min&lt;/code&gt; bei &lt;code&gt;S30000U/min&lt;/code&gt; und einer Tiefenzustellung von nur &lt;code&gt;Z+1.5mm&lt;/code&gt;. Auch hier tauche ich mit &lt;code&gt;20°&lt;/code&gt; ins Material ein.&lt;/p&gt;&#10;&lt;h3 id="fertigung"&gt;Fertigung&lt;/h3&gt;&#10;&lt;p&gt;Das Arbeiten im mir bis Dato noch völlig unbekannten Material &amp;ldquo;Bambus&amp;rdquo; ging überraschend leicht von der Hand. Dank meiner noch neuen und sehr scharfen Fräser waren keinerlei Ausrisse zu beklagen und das Material selbst hatte ebenfalls kaum Defekte oder Schadstellen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/ayYr653wLVLzVpmiMkhyz8" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Klingenbutler CNC-fräsen&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Bei der Bearbeitung entstanden recht feine Späne, die ein wenig an Staub von der Verarbeitung von MDF-Platten erinnern. Ich bin mir noch nicht sicher, ob ich mit der Drehzahl weiter heruntergehen sollte oder ob dies an den geraden Schneiden des Fräsers liegt. Dieser muss Späne beim Fräsen in der Vollnut unter Umständen mehrere Umdrehungen lang mit sich führen, bevor er sie in der Nut hinter sich lassen kann.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-10-14_klingenbutler.jpg" alt="Image: Klingenbutler V1.1 Bamboo edition" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Bohrungen jedenfalls sind mit diesem Fräser und unveränderten Parametern nicht zu empfehlen. Sämtliche Bohrlöcher haben verkohlte Ränder, was auf viel zu hohe Drehzahlen schließen lässt. Zudem habe in einem Stück gebohrt, anstatt den Fräser mehrfach zurückzuziehen. Nehmen wir den Dreisatz zur Hilfe: Beim Vorschub von &lt;code&gt;FZ+2000mm/min&lt;/code&gt;, &lt;code&gt;S30000/U/min&lt;/code&gt; und &lt;code&gt;Z7.5mm&lt;/code&gt; führt der Fräser &lt;code&gt;112.5&lt;/code&gt; Umdrehungen im Bohrloch aus, reiben die Schneiden also 225 mal sinnlos an der Wand des Bohrloches entlang - abtragen tut ja nur die Zentrumsschneide unten. Zudem werden die Späne ja nicht ausgeworfen sondern die ganze Zeit über mitgeführt.&lt;/p&gt;&#10;&lt;p&gt;Für den nächsten Prototypen werde ich Bohrlöcher also entweder mit Rückzügen und drastisch reduzierter Drehzahl fertigen oder schlicht einen kleineren Fräser verwenden.&lt;/p&gt;&#10;&lt;h3 id="ergebnis"&gt;Ergebnis&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-10-14_bambusdetail.jpg" alt="Image: Klingenbutler detail view (underside)" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Das kann sich sehen lassen. Ich habe die Fase zum Kantenbrechen aus der Fertigung in Acrylglas übernommen. Sie fällt mit &lt;code&gt;0.5mm&lt;/code&gt; sehr schmal aus. Trotzdem zeigt sich auch im Detail ein sehr sauberes Bild. Ober- und Unterschale des Klingenbutler lassen sich weich gegeneinander verschieben und auch der Klemmer, mein Sorgenkind noch &lt;a href="https://makertube.net/w/64TS5d5cp2eNqf8SSPvY77" target="_blank" rel="noopener noreferrer" class="external-link"&gt;aus diesem Video&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ließ sich ohne Ausrisse fräsen.&lt;/p&gt;&#10;&lt;p&gt;Der Zusammenbau geht problemlos vonstatten und auch die Holzschrauben lassen sich leicht in die vorgebohrten Löcher eindrehen. Der Klingenbutler aus Bambus hält an der Duschstange sogar noch besser als sein Pendant aus Acrylglas - der raueren Oberfläche und höheren Materialstärke sei dank.&lt;/p&gt;&#10;&lt;p&gt;Bleibt abzuwarten, ob das Material auch im Feuchtraum langlebig genug ist. Für Schneidbretter in der Küche wird es ja noch immer gern verwendet.&lt;/p&gt;&#10;&lt;h2 id="ausblick"&gt;Ausblick&lt;/h2&gt;&#10;&lt;p&gt;Die Verwendung von Bambus hat mich sehr motiviert hinterlassen: Ein toller Werkstoff. Hart und fest aber dabei leicht, gut maschinell zu bearbeiten, formstabil und kaum Materialdefekte: So wünscht man sich das Arbeiten mit Naturmaterialien!&lt;/p&gt;&#10;&lt;p&gt;Dieser Prototyp landet nun zum Dauertest bei uns in der Dusche. In der Zwischenzeit verfeinere ich das Design weiter.&lt;/p&gt;&#10;&lt;h3 id="version-12"&gt;Version 1.2&lt;/h3&gt;&#10;&lt;p&gt;Für V1.2 habe ich folgende kleine Verbesserungen in Planung:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Bohrungen: Kleineren Fräser wählen (zu Lasten der Zykluszeit)&lt;/li&gt;&#10;&lt;li&gt;Befestigung: Ausschnitt fräsen, sodass das Rändelrad nicht mehr übersteht&lt;/li&gt;&#10;&lt;li&gt;Aussparung innen an der Oberschale ist überflüssig&lt;/li&gt;&#10;&lt;li&gt;Unterschiedliche Radien mit G2-Kurven übergehen lassen&lt;/li&gt;&#10;&lt;li&gt;Rastnasen durch harmonischere, runde Ablaufrinne ersetzen&lt;/li&gt;&#10;&lt;li&gt;Maserung ausrichten: Alle Teile &amp;ldquo;senkrecht&amp;rdquo; fertigen&lt;/li&gt;&#10;&lt;li&gt;Die Klemmer nun in der ersten Aufspannung fräsen und um &lt;code&gt;0.04mm&lt;/code&gt; aufdicken&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Auch hier ist das Ziel, mittels stetiger Verbesserung hin zu einem guten Produkt zu kommen.&lt;/p&gt;&#10;</description></item><item><title>NVME-Festplatte kaputt?</title><link>https://blog.schallbert.de/broken-nvme/</link><pubDate>Tue, 10 Sep 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/broken-nvme/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-09-10_nvme-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: My NVME-SSD that suddenly broke down"&#10; title="NVME-Festplatte kaputt?" /&gt;&#10;&lt;h2 id="das-ereignis"&gt;Das Ereignis&lt;/h2&gt;&#10;&lt;p&gt;Mitte April 2024:&lt;/p&gt;&#10;&lt;p&gt;Nach einem Systemupdate fährt mein Laptop plötzlich nicht mehr hoch. Der Bootloader &lt;em&gt;grub-core&lt;/em&gt; meldet, er habe Probleme beim Schreiben von Sektoren.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;error: ../grub-core/efi/efidisk.c:638: failure writing sector 0xXXXXXXXX&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Beim Reboot bleibt mir der Bildschirm nach Anzeige des Firmenlogos mit blinkendem Cursor schwarz. Wenn ich hinter die Maske schaue, dann schlägt das Laden diverser Services fehl:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2024-09-10_nvme-boot-errors.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Boot errors due to read-only nvme&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="erste-analyse"&gt;Erste Analyse&lt;/h2&gt;&#10;&lt;p&gt;Ich vermutete ein Problem beim Update des Rechners und führte aus dem &lt;a href="https://en.wikipedia.org/wiki/UEFI" target="_blank" rel="noopener noreferrer" class="external-link"&gt;UEFI&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; von Lenovo heraus einen Festplattentest durch. Ergebnis:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;The media check is complete, the result is: FAIL&#10;&lt;/code&gt;&lt;/pre&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-09-10_diskcheck-fail.jpg" alt="Image: disk check tool diagnoses a defective device" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Eher kein Problem mit dem Betriebssystem sondern eines mit der Platte. Mist. Muss ich um meine Daten bangen? Ich habe zwar Backups herumliegen, das letzte ist aber drei Wochen alt und somit sind einige Stunden Arbeit auf der Kippe. Zudem kostet mich ein Wiederaufsetzen einiges an Zeit. Daher probiere ich erst einmal, ob sich noch etwas retten lässt.&lt;/p&gt;&#10;&lt;h3 id="selbsttest"&gt;Selbsttest&lt;/h3&gt;&#10;&lt;p&gt;Ich lasse also die &lt;a href="https://en.wikipedia.org/wiki/Self-Monitoring,_Analysis_and_Reporting_Technology" target="_blank" rel="noopener noreferrer" class="external-link"&gt;SMART&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Tests für die Platte mit &lt;code&gt;smartctl -x /dev/nvm0&lt;/code&gt; durchlaufen und weiß nun, dass sie etwa &lt;code&gt;7600&lt;/code&gt; Betriebsstunden hinter sich und &lt;code&gt;240GB&lt;/code&gt; nutzbare Kapazität hat. Die unter dem Namen &lt;code&gt;nvme0n1&lt;/code&gt; eingesetzte Disk meint, sie sei nun &amp;ldquo;read-only&amp;rdquo;. Und zwar sämtliche Partitionen inklusive Kernel. Na klasse. Das Ergebnis meiner Online-Suche war in etwa:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Die Platte ist irreparabel beschädigt. Es muss eine Neue her.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h3 id="reparaturversuch"&gt;Reparaturversuch&lt;/h3&gt;&#10;&lt;p&gt;Man muss ja nicht alles glauben, was im Netz steht. Also nehme ich das Lenovo-eigene Reparaturwerkzeug für Festplatten zur Hand. Es lässt sich aus dem UEFI heraus aufrufen und firmiert unter dem Namen &lt;a href="https://download.lenovo.com/pccbbs/thinkvantage_en/userguide_uefi_v04.08.000.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Bad Block Recovery Tool&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Das Programm scannt die Platte auf defekte Sektoren und versucht, betroffene Blöcke wiederherzustellen. Der Durchlauf benötigt für die letzten 10% fast eine halbe Stunde.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-09-10_badblockrecovery_summary.jpg" alt="Image: Bad Block Recovery tool&amp;#39;s summary for my disk" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-09-10_badblockrecovery_detail.jpg" alt="Image: Bad Block Recovery tool&amp;#39;s output messages" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Ergebnis: Wiederherstellung fehlgeschlagen. Ich führe das Werkzeug erneut aus. Und noch ein drittes Mal im langsamen &lt;code&gt;pedantic&lt;/code&gt;-Modus. Immer wieder ohne Erfolg. Ständig bekomme ich &lt;code&gt;critical medium error&lt;/code&gt; angezeigt.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-09-10_badblockrecovery_log.jpg" alt="Image: Bad Block Recovery tool&amp;#39;s log" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Ob dies alles nicht klappt, weil sich die Platte in den Read-only mode gesetzt hat?&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Da ich nicht mehr booten kann und keine Konsole zur Verfügung habe, sind meine Möglichkeiten mit Bordmitteln nun erschöpft.&lt;/p&gt;&#10;&lt;h3 id="einwurf-der-read-only-mode-bei-ssds"&gt;Einwurf: Der Read-only mode bei SSDs&lt;/h3&gt;&#10;&lt;p&gt;Laut Internet &lt;a href="https://www.disktuna.com/a-write-protected-ssd-nvme-read-only/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;(DiskTuna)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist der &lt;em&gt;read only mode&lt;/em&gt; zur Schadensbegrenzung vorhanden. Stellt der Controller des Speichergerätes einen schwerwiegenden Fehler fest, versetzt er die Platte in Nur-Lesezugriff. Somit wird das Risiko minimiert, dass weitere Blöcke korrumpiert oder mit in den Tod gerissen werden.&lt;/p&gt;&#10;&lt;p&gt;Im Forum &lt;a href="https://superuser.com/questions/1802470/ssd-in-read-only-mode-after-power-failure" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Superuser&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; beschreibt jemand einen identischen Fehler zu meinem, und tatsächlich ist da wohl nichts mehr zu machen.&lt;/p&gt;&#10;&lt;h2 id="datenrettung-per-boot-stick"&gt;Datenrettung per Boot-Stick&lt;/h2&gt;&#10;&lt;p&gt;Weiter geht&amp;rsquo;s mit einem USB-Stick, den ich von einem anderen Rechner aus zum Linux-Boot-Stick mache. Ich stopfe ihn an meinen Laptop und kann ihn endlich wieder hochfahren. Als Medium zur Datenrettung schließe ich eine externe Festplatte an.&lt;/p&gt;&#10;&lt;h3 id="übersicht-verschaffen-mit-fdisk"&gt;Übersicht verschaffen mit &lt;em&gt;fdisk&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;Zuerst schaue ich, ob alle Platten und Partitionen auftauchen. Dafür tippe ich das Kommando &lt;code&gt;sudo fdisk -l&lt;/code&gt; in die Konsole.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-09-10_fdisk.jpg" alt="Image: fdisk&amp;#39;s output on my live system" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Glück gehabt, alles ist noch da! Vielleicht kann ich die Dateien ja einfach von der defekten Festplatte herunter kopieren?&lt;/p&gt;&#10;&lt;h3 id="kopieren-mit-cp"&gt;Kopieren mit &lt;em&gt;cp&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;Probieren wir&amp;rsquo;s aus.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo cp -r -v /mnt/nvme0n1p3/Documents/my/important/files /mnt/externalHdd/save&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ein paar Sekunden denke ich, dass alles glatt geht. Doch dann bricht &lt;code&gt;cp&lt;/code&gt; mit Fehlermeldung ab, das Volume sei nicht verfügbar. Tatsächlich ist die Platte nicht mehr gemountet. Ich kann sie allerdings direkt wieder finden und mounten. Ein paar Versuche später habe ich meine wirklich wichtigen Daten gesichert, der Kopiervorgang bricht aber stets nach ein paar Sekunden ab. Für die ganze Platte würde mir dieses Vorgehen viel zu lange dauern.&lt;/p&gt;&#10;&lt;p&gt;Genau dieses Verhalten - mounten, ein paar Sekunden kopieren, Platte verschwindet - habe ich online in ein paar Foreneinträgen bestätigt bekommen.&lt;/p&gt;&#10;&lt;p&gt;Also muss eine andere Lösung her.&lt;/p&gt;&#10;&lt;h3 id="daten-retten-mit-ddrescue"&gt;Daten retten mit &lt;em&gt;ddrescue&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-09-10_ddrescue.jpg" alt="Image: ddrescue output when saving my broken SSD&amp;#39;s files to an external HDD." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Nach kurzer Online-Suche installiere ich das Programm &lt;a href="https://www.gnu.org/software/ddrescue/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;ddrescue&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;In der zugehörigen Dokumentation stehen einige Dinge sehr klar drin. Dass man z.B. verstanden haben sollte was es genau tut, bevor man es verwendet. Oder das Folgende:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Never try to repair a file system on a drive with I/O errors; you will probably lose even more data. &amp;quot; - ddrescue Manual&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Jetzt stellt sich für mich die Frage, ob ich oben mit dem Block-Reparaturversuch genau so einen Fehler begangen habe. Oder bin ich mit der Bad Block Recovery auf der sicheren Seite, da hier von Datenblöcken auf der Platte und nicht vom &amp;ldquo;darüber liegenden&amp;rdquo; Dateisystem die Rede ist?&#10;Hier steht auch, dass die sogenannte &lt;em&gt;Mapfile&lt;/em&gt; eine Kernfunktion des Programmes ist. Nach Abbruch, Neustart, für die Zusammenführung von Backups und zum Verkürzen der Datenrettung bietet sie einen großen Zeitvorteil. Daher sei es keine schlechte Idee, sich die Mapfile schon bei Erstellung einer Offlinhe-Sicherheitskopie zu erzeugen und gut wegzulegen. Insgesamt fand ich das Tool extrem hilfreich, effektiv und leicht zu bedienen und kann es an dieser Stelle nur weiterempfehlen.&lt;/p&gt;&#10;&lt;p&gt;Zurück zur Sache: Ich finde einen ganz einfachen Befehl, die komplette Datenpartition meines beschädigten NVME-Speichers auf eine externe Festplatte zu kopieren:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddrescue --sparse /dev/nvme0n1p3 /run/media/liveuser/myExternalHDD/datapartition&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Die &lt;code&gt;--sparse&lt;/code&gt;-Option überspringt mit Nullen belegte Bereiche der Quellplatte und kann so eine Menge Platz auf dem Zieldatenträger einsparen.&#10;Das Programm benötigt eine Viertelstunde für den kompletten Durchlauf. Es kann wie oben im Bild zu erkennen tatsächlich &lt;code&gt;99.6%&lt;/code&gt; der Daten retten und auch mehrere Monate später habe ich die restlichen &lt;code&gt;0.4%&lt;/code&gt; noch nicht vermisst. Glück gehabt.&lt;/p&gt;&#10;&lt;h3 id="überprüfung-output-von-ddrescue-ansehen"&gt;Überprüfung: Output von &lt;em&gt;ddrescue&lt;/em&gt; ansehen&lt;/h3&gt;&#10;&lt;p&gt;Jedenfalls legt das Programm am Ende eine einzige Datei auf meiner externen Platte ab: &lt;code&gt;datapartition.img&lt;/code&gt;&#10;Dieses Image kann ich mit &lt;code&gt;fdisk -l&lt;/code&gt; anschauen:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;datapartition.img: 236.89 GiB, 254356226048 bytes, 496789504 sectors&#10;Units: sectors of 1 * 512 = 512 bytes&#10;Sector size (logical/physical): 512 bytes / 512 bytes&#10;I/O size (minimum/optimal): 512 bytes / 512 bytes&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Alles da, das ist ja schon einmal gut. Ob denn nun auch alles drin ist?&lt;/p&gt;&#10;&lt;p&gt;Dafür mounte ich die Partition mal und schaue mich per &lt;code&gt;cd&lt;/code&gt; und &lt;code&gt;ls -l&lt;/code&gt; darin um:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo mkdir /mnt/olddisk &lt;span style="color:#75715e"&gt;# Mount point of the rescued data image&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo mount /run/media/liveuser/myExternalHDD/datapartition.img /mnt/olddisk&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Zum Glück war alles drin, was drin sein sollte. Nun konnte ich das Image unmounten, den Rechner herunterfahren und eine neue Festplatte einbauen lassen.&lt;/p&gt;&#10;&lt;h2 id="die-neue-festplatte"&gt;Die neue Festplatte&lt;/h2&gt;&#10;&lt;p&gt;Auch hier habe ich noch einmal Glück gehabt: Auf meinem Gebrauchtlaptop war noch Garantie und so bekam ich ihn binnen einer Woche mit einer neuen (und doppelt so großen) Festplatte zurück gesendet. Da ich so große Angst vor Datenklau bekam, hatte ich meine alte Platte vor dem Versenden ausgebaut. Dabei kam ich mir ganz schön paranoid vor.&lt;/p&gt;&#10;&lt;p&gt;Diese Arbeit hätte ich mir aber vielleicht sparen können, denn &lt;em&gt;ddrescue&lt;/em&gt; hat auch für paranoide Leute wie mich eine Software-Lösung parat: Die Option &lt;code&gt;--fill-mode&lt;/code&gt;.&#10;Hiermit können nach erfolgter Rettung die &amp;ldquo;noch guten&amp;rdquo; Blöcke und Sektoren gelöscht werden. Nur die ohnehin defekten Sektoren bleiben erhalten. Aus der &lt;a href="https://www.gnu.org/software/ddrescue/manual/ddrescue_manual.html#Fill-mode" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Online-Dokumentation&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ddrescue --fill-mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;+ --force /dev/zero bad_drive mapfile&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ob dies allerdings auch einer Platte wie meiner funktioniert hätte, die der On-Board-Controller auf &amp;ldquo;read-only&amp;rdquo; gesetzt hat, ist mir nicht klar. Egal.&lt;/p&gt;&#10;&lt;p&gt;Ich kann jetzt jedenfalls mit einem voll funktionsfähigen Laptop weiter Blog-Einträge verfassen ☺️.&lt;/p&gt;&#10;</description></item><item><title>Giteas act_runner: Startprobleme</title><link>https://blog.schallbert.de/fix-gitea-runner/</link><pubDate>Fri, 30 Aug 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/fix-gitea-runner/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-08-30_badgateway_runner-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Bad Gateway error message from Gitea&amp;#39;&amp;#39;s act_runner at startup"&#10; title="Giteas act_runner: Startprobleme" /&gt;&#10;&lt;h2 id="das-problem"&gt;Das Problem&lt;/h2&gt;&#10;&lt;p&gt;Seitdem ich diese Seite in Eigenregie betreibe, habe ich nach Neustart des &lt;em&gt;Gitea&lt;/em&gt;-Containers das Problem, dass der aktivierte &lt;em&gt;act_runner&lt;/em&gt; manchmal nicht mit hochfährt. Er bricht dann unter Angabe von Status Code &lt;code&gt;-1&lt;/code&gt; ab und meine Workflows werden nicht abgearbeitet, sodass ich z.B. keine Artikel bauen oder veröffentlichen kann.&lt;/p&gt;&#10;&lt;p&gt;Die Fehlermeldung ist immer dieselbe:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;[...]&#10;runner-1 | time=&amp;#34;2024-08-26T10:09:46Z&amp;#34; level=info msg=&amp;#34;Starting runner daemon&amp;#34;&#10;runner-1 | time=&amp;#34;2024-08-26T10:09:46Z&amp;#34; level=error msg=&amp;#34;fail to invoke Declare&amp;#34; error=&amp;#34;unavailable: 502 Bad Gateway&amp;#34;&#10;runner-1 | Error: unavailable: 502 Bad Gateway&#10;runner-1 exited with code 1&#10;gitea | 2024/08/26 10:09:46 cmd/web.go:242:runWeb() [I] Starting Gitea on PID: 16&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Der Runner kann sich also nicht bei &lt;em&gt;Gitea&lt;/em&gt; einklinken. Wenn ich alle Services über Docker per &lt;code&gt;docker restart&lt;/code&gt; erneut starte, meint der Container für &lt;code&gt;act_runner&lt;/code&gt;, ihm fehle dafür ein Netzwerk mit ID &lt;code&gt;&amp;lt;ellenlanger Hexcode hier&amp;gt;&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="die-kurzfristige-lösung"&gt;Die kurzfristige Lösung&lt;/h2&gt;&#10;&lt;p&gt;Ich hatte die letzten Monate keine Zeit dem nachzugehen. Daher half ich mir stets damit, folgendes doppelt auszuführen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# schallbert server-console&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker compose -f /path/to/giteas/composefile up -d&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Beim zweiten Anlauf dann lief der Runner wiederholbar erfolgreich an (Gitea war ja schon betriebsbereit):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;gitea | 2024/08/26 10:38:17 routers/init.go:116:InitWebInstalled() [I] Git version: 2.45.2 (home: /data/gitea/home)&#10;runner-1 | time=&amp;#34;2024-08-26T10:38:22Z&amp;#34; level=info msg=&amp;#34;Starting runner daemon&amp;#34;&#10;runner-1 | time=&amp;#34;2024-08-26T10:38:22Z&amp;#34; level=info msg=&amp;#34;runner: action-runner, with version: v0.2.10, with labels: [ubuntu-latest], declare successfully&amp;#34;&#10;runner-1 exited with code 0&#10;runner-1 | time=&amp;#34;2024-08-26T10:40:32Z&amp;#34; level=info msg=&amp;#34;Started runner daemon&amp;#34;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dies ist natürlich nicht so schön und muss händisch von mir gemacht werden. Doch was ist die Ursache?&lt;/p&gt;&#10;&lt;h2 id="ursachenforschung"&gt;Ursachenforschung&lt;/h2&gt;&#10;&lt;p&gt;Zuerst hatte ich den Reverse Proxy von &lt;em&gt;Caddy&lt;/em&gt; im Verdacht, da er &lt;em&gt;Gitea&lt;/em&gt; an das Internet anbindet. Doch tatsächlich ist &lt;em&gt;Gitea&lt;/em&gt; selbst das Problem: Es ist zum Zeitpunkt, zu dem &lt;em&gt;Docker&lt;/em&gt; den &lt;em&gt;runner&lt;/em&gt; Container startet, noch gar nicht fertig hochgefahren. Also lese ich nach, wie ich Abhängigkeiten in Docker besser darstellen kann.&lt;/p&gt;&#10;&lt;h2 id="lösung-abhängigkeiten-abbilden"&gt;Lösung: Abhängigkeiten abbilden&lt;/h2&gt;&#10;&lt;p&gt;Zuerst versuche ich es mit dem Einbinden der Änderung in docker compose.&lt;/p&gt;&#10;&lt;h3 id="depends_on"&gt;depends_on&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# gitea/docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;## service: runner&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;## [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;depends_on&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;gitea&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;condition&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service_started&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Doch noch immer startet der &lt;em&gt;act_runner&lt;/em&gt; wohl zu früh. Docker stellt hier nämlich lediglich sicher, dass &lt;em&gt;Gitea&lt;/em&gt; gestartet wurde, nicht aber, dass es hochgefahren ist und stabil läuft.&lt;/p&gt;&#10;&lt;h3 id="healthcheck"&gt;Healthcheck&lt;/h3&gt;&#10;&lt;p&gt;Ich muss folglich sicherstellen, dass &lt;em&gt;Gitea&lt;/em&gt; betriebsbereit ist. Dies geht prima mit der Bedingung &lt;code&gt;service_healthy&lt;/code&gt;. Ich passe also die Instruktionen für den Runner wie folgt an:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# gitea/docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;## service: runner&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;## [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;depends_on&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;gitea&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;condition&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service_healthy&lt;/span&gt; &lt;span style="color:#75715e"&gt;# required so runner can attach to gitea&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;restart&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Der &lt;code&gt;service_healthy&lt;/code&gt; Qualifier ist ein Rückgabewert der &lt;code&gt;healthcheck&lt;/code&gt; Funktion. Diese besteht aus einem sogenannten &lt;code&gt;test&lt;/code&gt; und ein paar Begleitparametern, in welchem Intervall wie oft und nach welcher Zeit geprüft werden soll, ob der Dienst nun betriebsbereit ist.&lt;/p&gt;&#10;&lt;p&gt;Ich habe als Test ein einfaches Kommando gewählt mit der Annahme, dass Gitea (und damit implizit auch Caddy) fertig hochgefahren ist, sobald &lt;code&gt;curl&lt;/code&gt; seine Webseite aufrufen kann.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# gitea/docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## service: gitea&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;healthcheck&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;test&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;CMD&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;curl&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;-f&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;https://git.schallbert.de/&amp;#34;&lt;/span&gt;] &lt;span style="color:#75715e"&gt;# checks if gitea is available&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;interval&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;retries&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;start_period&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;timeout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mit diesen Zeilen prüft &lt;em&gt;Docker&lt;/em&gt;, ob Gitea &amp;ldquo;healthy&amp;rdquo; ist und startet den &lt;em&gt;act_runner&lt;/em&gt; entsprechend später. Das oben dargestellte Problem ist auf meinem Server seitdem komplett behoben und &lt;em&gt;Gitea&lt;/em&gt; sowie &lt;em&gt;runner&lt;/em&gt; verhalten sich zuverlässig und stabil.&lt;/p&gt;&#10;</description></item><item><title>Fehler-Logdateien versenden</title><link>https://blog.schallbert.de/server-deploy-logging/</link><pubDate>Tue, 20 Aug 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/server-deploy-logging/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-08-20_deploy_logging-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: A notification message sent on error"&#10; title="Fehler-Logdateien versenden" /&gt;&#10;&lt;p&gt;In diesem Artikel schaue ich mir an, wie ich &amp;ldquo;Monitoring&amp;rdquo; für meinen Server einrichte. Anwendungen und Dienste sollen mir im Fehlerfalle Benachrichtigungen senden können.&lt;/p&gt;&#10;&lt;h2 id="worum-geht-es-hier"&gt;Worum geht es hier?&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Sendemechaniken kennenlernen und auswählen&lt;/li&gt;&#10;&lt;li&gt;Testnachrichten verfassen und die Automation verifizieren&lt;/li&gt;&#10;&lt;li&gt;Fehlerbericht von &lt;em&gt;Borgmatic&lt;/em&gt; automatisch senden&lt;/li&gt;&#10;&lt;li&gt;Senden von Runner-logs durch &lt;em&gt;Gitea&lt;/em&gt;&lt;/li&gt;&#10;&lt;li&gt;Benachrichtigung bei Login auf meinem Server per &lt;em&gt;ssh&lt;/em&gt;&lt;/li&gt;&#10;&lt;li&gt;Erstellen und Senden eigener Logs zu Server-Update / Server-Fehlern&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="verifikation-und-überwachung"&gt;Verifikation und Überwachung&lt;/h3&gt;&#10;&lt;p&gt;Ich möchte nach dem Ausführen einer Automation wissen, ob sie erfolgreich durchgeführt wurde und alle Programme und Dienste ihre Arbeit erwartungsgemäß aufgenommen haben. Dies soll für jegliche Automation gelten - ob es sich konkret um das Senden eines Updates, die Anfertigung automatischer Backups oder eine Action von &lt;em&gt;Gitea&lt;/em&gt; handelt, muss egal sein.&lt;/p&gt;&#10;&lt;p&gt;Normalerweise würde ich für so etwas Berichtsmechanismen von &lt;em&gt;act_runner&lt;/em&gt; verwenden. Im Falle eines Server-Updates steht der Runner durch den bereits im Artikel &lt;a href="https://blog.schallbert.de/server-config-deploy/"&gt;Serverkonfiguration ausrollen&lt;/a&gt; erwähnten &lt;a href="https://blog.schallbert.de/server-config-version-control/#vor%c3%bcberlegungen"&gt;Zirkelbezug&lt;/a&gt; nicht zur Verfügung, da zeitweise alle Anwendungen heruntergefahren werden müssen.&lt;/p&gt;&#10;&lt;p&gt;Zudem kann es sein, dass Anwendungen eigene Vorgehensweisen haben, Monitoring zu betreiben. Also muss ich mir Mechanismen anschauen, mit denen ich leicht an die Informationen herankomme.&lt;/p&gt;&#10;&lt;h3 id="was-zeichnet-ein-gutes-monitoring-für-mich-aus"&gt;Was zeichnet ein gutes Monitoring für mich aus?&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Es ist unaufdringlich, meldet sich also nur im Fehlerfall oder bei ungewöhnlichen Vorgängen.&lt;/li&gt;&#10;&lt;li&gt;Es liefert spezifische Informationen und gut zu verstehende Fehlermeldungen.&lt;/li&gt;&#10;&lt;li&gt;Es nutzt einen Nachrichtenkanal, der auch bei Absturz des zu überwachenden Systems funktioniert.&lt;/li&gt;&#10;&lt;li&gt;Es stellt Berichte und Fehlermeldungen isoliert von anderen Themen dar und vermischt nichts.&lt;/li&gt;&#10;&lt;li&gt;Es fasst sich kurz.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="methoden-automatischer-berichterstattung"&gt;Methoden automatischer Berichterstattung&lt;/h2&gt;&#10;&lt;p&gt;Diesen Abschnitt gliedere ich ich auf. Im Allgemeinen Teil gehe ich auf das mir auf dem Ubuntu-Server zur Verfügung stehende Bordwerkzeug zum asynchronen Monitoring ein. Danach schaue ich mir die bei meinen Diensten teils eingebauten Lösungen bzw. mit ihnen kompatible Anwendungen an. Dabei möchte ich mich nicht auf das klassische Instrument der E-Mail beschränken, sondern auch kurz &amp;ldquo;modernere&amp;rdquo; Kommunikationskanäle wie Messengerprogramme oder RSS-Feed beleuchten.&lt;/p&gt;&#10;&lt;h3 id="mail-per-konsole---curl"&gt;Mail per Konsole - &lt;em&gt;curl&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;Versand von Mails als Benachrichtigungen sind in vielen Firmen üblich. Dies geht mit Linux meist sogar ganz ohne zusätzliche Programme: Das standardmäßig vorhandene &lt;a href="https://curl.se/docs/manpage.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;curl&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; kann hier bereits weiterhelfen.&#10;&lt;em&gt;curl&lt;/em&gt; it ein Programm zum Datentransfer von oder zu einem Server. Gebe ich meinen Blog als Ziel ein, bekomme ich die HTML-Seite als Textdatei auf die Konsole ausgegeben:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl https://blog.schallbert.de&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bei der Gelegenheit fällt mir auf, wie viel unnötige Daten meine Blogsoftware erzeugt. Da werde ich später mal aufräumen müssen. Zurück zum Thema: Man kann &lt;em&gt;curl&lt;/em&gt; allerdings auch nutzen, um ein beliebiges Webbackend anzusprechen - Zum Beispiel einen Mailserver:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# copied from https://stackoverflow.com/questions/8260858/how-to-send-email-from-terminal&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl --url &lt;span style="color:#e6db74"&gt;&amp;#39;smtps://smtp.gmail.com:465&amp;#39;&lt;/span&gt; --ssl-reqd &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --mail-from &lt;span style="color:#e6db74"&gt;&amp;#39;from-email@gmail.com&amp;#39;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --mail-rcpt &lt;span style="color:#e6db74"&gt;&amp;#39;to-email@gmail.com&amp;#39;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --user &lt;span style="color:#e6db74"&gt;&amp;#39;from-email@gmail.com:YourPassword&amp;#39;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -T &amp;lt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;echo -e &lt;span style="color:#e6db74"&gt;&amp;#39;From: from-email@gmail.com\nTo: to-email@gmail.com\nSubject: Curl Test\n\nHello&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dies funktioniert nur dann auch für meinen Mail-Provider, wenn ich Logins durch externe Clients zulasse. Google nennt diese z.B. &amp;ldquo;weniger sichere Apps&amp;rdquo;. Ich bin wie in meinem Beitrag &lt;a href="https://blog.schallbert.de/server-config-version-control/#frisch-ans-werk"&gt;Server-Konfiguration mit Git&lt;/a&gt; beschrieben kein Fan davon, Secrets irgendwo hart hineinzuschreiben - daher würde ich den direkten Weg über &lt;em&gt;curl&lt;/em&gt; eher nicht nutzen.&lt;/p&gt;&#10;&lt;h3 id="mail-per-konsole---mail-mailx-mailutils-swaks"&gt;Mail per Konsole - &lt;em&gt;mail&lt;/em&gt;, &lt;em&gt;mailx&lt;/em&gt;, &lt;em&gt;mailutils&lt;/em&gt;, &lt;em&gt;swaks&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;Wer all die Konfigurationsinformationen für den Server und Secrets nicht immer mitgeben möchte, dem stehen diverse handliche Werkzeuge für die Konsole wie &lt;a href="https://mailutils.org/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;mailutils&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; oder &lt;a href="https://github.com/jetmore/swaks" target="_blank" rel="noopener noreferrer" class="external-link"&gt;swaks&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zur Verfügung. Hier wird die Verbindung zum Mailserver über das Werkzeug einmalig konfiguriert und kann in z.B. in Umgebungsvariablen abgelegt werden. Die Syntax variiert von Programm zu Programm, hinten kommt aber stets eine Mail heraus.&lt;/p&gt;&#10;&lt;p&gt;An sich eine simple Lösung. Der einzige große Nachteil dabei für mich ist, dass sich hier Informationsdomänen vermischen. Ich hätte nur ungern noch einen &amp;ldquo;Berichtsthread&amp;rdquo; in meinen Mails, nach dem ich im Wust der Nachrichten irgendwann würde suchen müssen. Bei Benachrichtigungen auf anderen Kanälen kann ich zudem ein automatisches Verfallsdatum einstellen, sodass sie nach eingestellter Zeit aus meiner Liste verschwinden.&lt;/p&gt;&#10;&lt;h3 id="rss-feed-erzeugen"&gt;RSS Feed erzeugen&lt;/h3&gt;&#10;&lt;p&gt;Ungewöhnlich aber möglich: Ich könnte das Monitoring als &lt;a href="https://en.wikipedia.org/wiki/RSS" target="_blank" rel="noopener noreferrer" class="external-link"&gt;RSS-Feed&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; wie meinen Blog auch (&lt;a href="https://blog.schallbert.de/index.xml"&gt;schallberts-blog-feed&lt;/a&gt;) z.B. per Jekyll-Instanz erzeugen und als Website online stellen. Dieser wäre leicht zu abonnieren, mit praktisch jedem Reader zu lesen und ich könnte ihn sogar für jede Applikation ganz einfach getrennt vorsehen. Doch es gibt offensichtliche Nachteile:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Hoher Aufwand: Gitea runner mit Jekyll-Instanz, Webserver und Subdomain erforderlich.&lt;/li&gt;&#10;&lt;li&gt;Öffentlich verfügbar: Plötzlich werden Bauvorgänge, Updates, Upgrades und Fehlermeldungen für jedermann zugänglich.&lt;/li&gt;&#10;&lt;li&gt;Fehleranfällig: Schmiert Gitea, der Runner, mein Proxy oder der Webserver ab, bekomme ich keine Berichte.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Spätestens mit dem letzten Punkt ist diese Option für mich raus. Ich möchte ja gerade dann einen Bericht bekommen, wenn meine Applikationen nicht das tun, was sie sollen.&lt;/p&gt;&#10;&lt;p&gt;Werfen wir nun mal einen Blick auf die von mir bereits betriebenen Anwendungen und schauen, wie sie dieses Problem angehen.&lt;/p&gt;&#10;&lt;h3 id="borgmatic"&gt;&lt;em&gt;borgmatic&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;Borgmatic bringt von Haus aus Kompabitilität zu einer Menge &lt;a href="https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Monitoring-Optionen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; mit. Darunter befinden sich &lt;a href="https://github.com/caronc/apprise" target="_blank" rel="noopener noreferrer" class="external-link"&gt;apprise&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://ntfy.sh/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;ntfy&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://healthchecks.io/docs/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;healthchecks&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://cronitor.io/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;cronitor&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://www.pagerduty.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;pagerduty&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://cronhub.io/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;cronhub&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und &lt;a href="https://grafana.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;grafana&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;em&gt;Apprise&lt;/em&gt; ist eine Bibliothek. Sie ist quelloffen und kann von einer bestehenden Anwendung als Abhängigkeit eingebunden werden. Wie ein Adapter ermöglicht sie die asynchrone Kommunikation der Anwendung mit diversen verschiedenen Kommunikationsdiensten wie SMS, Mail, Messenger (z.B. &lt;a href="https://signal.org/de/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Signal&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;), diverse Heimautomation oder den Benachrichtigungssystemen diverser Betriebssysteme. Letzteres funktioniert allerdings nur auf der lokalen Maschine. Der Trigger für die Kommunikation muss dabei stets von der Anwendung ausgehen.&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;ntfy&lt;/em&gt; ist ein Push-Benachrichtigungsdienst. Er ist quelloffen und kann sowohl selbst gehostet als auch über eine Webanwendung als Dienstleistung in Anspruch genommen werden. &lt;em&gt;Apprise&lt;/em&gt; z.B. unterstützt &lt;em&gt;ntfy&lt;/em&gt; als Kommunikationsdienst. Der Aufbau ist recht einfach und funktioniert wie bei &lt;a href="https://de.wikipedia.org/wiki/MQTT" target="_blank" rel="noopener noreferrer" class="external-link"&gt;MQTT&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; auch über einen Publication-Subscription bzw. Broker-Client Mechanismus, allerdings HTTP-basiert.&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;Healthchecks&lt;/em&gt; ist ein Dienst. Er ist quelloffen und kann selbst gehostet werden. Der Dienst ist dazu da, regelmäßige Aktivitäten zu überwachen und kann als Totmannschalter fungieren: Erfolgt anders als erwartet keine Rückmeldung vom überwachten Programm, so kann er selbst eine Fehlermeldung absetzen. Diese kann wiederum an diverse Kommunikationsdienste gesendet werden.&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;Cronitor&lt;/em&gt; ist eine Monitoringlösung und Webanwendung, die neben den von mir benötigten Benachrichtigungen eine Menge Analysetools, Leistungsmessungen und Metriken - zumeist gegen Geld - bereitstellt. Es gibt zwar einen kostenfreien &amp;ldquo;Hacker&amp;rdquo;-Account mit begrenztem Funktionsumfang, jedoch ist auch dieses Werkzeug mir entschieden zu groß und zu komplex.&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;PagerDuty&lt;/em&gt; sieht sich als kommerzielle &amp;ldquo;Operations&amp;rdquo; Plattform, die sowohl &amp;ldquo;Incident Management&amp;rdquo;, Automation, &amp;ldquo;Business Operations&amp;rdquo;, &amp;ldquo;AIOps&amp;rdquo; etc. bereitstellt. Ist für mich direkt raus. Spätestens beim Wort &amp;ldquo;AIOps&amp;rdquo; 😅&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;cronhub&lt;/em&gt; sieht mir wie eine kommerzielle Webanwendung aus, die ähnlich wie &lt;em&gt;Healthchecks&lt;/em&gt; Cron-Jobs erstellen, überwachen und bei Fehlern melden kann. Sie ist für mich uninteressant, denn sie scheint weder open source zu sein noch könnte ich sie selbst hosten.&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;Grafana&lt;/em&gt; ist eine Open-Source-Webanwendung, die entweder selbst gehostet oder als Cloud-Dienst verwendet werden kann. Obwohl viele größere Unternehmen und Projekte die Anwendung nutzen, ist sie für meine Zwecke um Größenordnungen zu umfang- und funktionsreich.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="fail2ban"&gt;&lt;em&gt;fail2ban&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;Fail2ban hat keine Automationslösung wie &lt;em&gt;borgmatic&lt;/em&gt;. Es erzeugt schlicht Logdateien die ausgewertet werden müssten, um Inhalte für Benachrichtigungen zu gewinnen. Mir fällt im Moment nichts ein, was ich unbedingt von Fail2ban wissen müsste. Daher erstelle ich mir hierfür erst einmal keine Berichte.&lt;/p&gt;&#10;&lt;h3 id="gitea"&gt;&lt;em&gt;Gitea&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;Bei Gitea lassen sich die Logdateien sehr feingliedrig konfigurieren. So können Access Logs getrennt von Service Logs, Repository Logs oder Action Logs herausgeschrieben und dann weiterverarbeitet werden. Als Benachrichtigungssystem bietet Gitea nach meiner Recherche ausschließlich einen &lt;a href="https://docs.gitea.com/next/administration/config-cheat-sheet#mailer-mailer" target="_blank" rel="noopener noreferrer" class="external-link"&gt;mailer&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; an. Dieser ist meiner Ansicht nach vor Allem für Repository- und Action Logs gedacht.&lt;/p&gt;&#10;&lt;p&gt;Auch hier würde es mir am meisten bringen, die Logs zu analysieren und bei Bedarf einen Bericht selbst zu erstellen.&lt;/p&gt;&#10;&lt;h3 id="server"&gt;Server&lt;/h3&gt;&#10;&lt;p&gt;Das Ausrollen meiner Server-Konfigurationsdateien übernehme ich per Skript ja sowieso schon selber. Also könnte ich auch hier im Fehlerfalle die Logs in eine Datei umleiten und sie dann einem Bericht in einen beliebigen Kanal anhängen.&lt;/p&gt;&#10;&lt;p&gt;Außerdem wären erfolgreiche Logins auf dem Server selbst eine Nachricht an mich wert. Dann kann ich sofort feststellen, ob ich das selbst war oder nicht.&lt;/p&gt;&#10;&lt;h2 id="die-auswahl-des-berichtsprogramms"&gt;Die Auswahl des Berichtsprogramms&lt;/h2&gt;&#10;&lt;p&gt;Ich muss nicht nur ein Monitoring-Programm heraussuchen, sondern auch einen Kommunikationsdienst wählen über den die Berichte versendet werden.&lt;/p&gt;&#10;&lt;h3 id="berichtsprogramm"&gt;Berichtsprogramm&lt;/h3&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Programm&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Vorteil&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Nachteil&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;em&gt;curl&lt;/em&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;simpel&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Konfiguration muss mitgegeben werden&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;funktioniert super einfach mit &lt;em&gt;ntfy&lt;/em&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Keine Überwachung bei Absturz des Servers&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Bordwerkzeug&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;kein Service erforderlich&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;em&gt;Apprise&lt;/em&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;ermöglicht Einbindung der Signal-Api&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Komplex im Zusammenspiel mit externen Diensten&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;kein Service erforderlich&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;em&gt;Healthchecks&lt;/em&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Kann als Watchdog/Totmannschalter agieren&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Registrierung erforderlich&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Kann lokal gehostet&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; werden&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lokales Hosting widerspricht Watchdog-Konzept&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Ich habe ja nur einen kleinen Server mit ein paar wenigen Anwendungen. Daher halte ich es hier so einfach wie möglich und wähle &lt;em&gt;Apprise&lt;/em&gt; für Dienste wie &lt;em&gt;borgmatic&lt;/em&gt;, die diese Bibliothek sowieso mitbringen und &lt;em&gt;curl&lt;/em&gt; für jene, deren Berichtsmechanik ich selbst programmieren muss.&lt;/p&gt;&#10;&lt;h3 id="kommunikationsweg"&gt;Kommunikationsweg&lt;/h3&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Programm&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Vorteil&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Nachteil&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;SMS&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Hohe Zuverlässigkeit&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Registrierung erforderlich&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Beschränkung auf wenige Zeichen&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Keine Anhänge möglich&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Keine Topics, Domänenvermischung&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Mail&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;einfach einzurichten&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schlechte Durchsuchbarkeit&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;RSS-Feed&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Gute Sortierbarkeit&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Kompliziert einzurichten, fehleranfällig&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Client sehr leichtgewichtig&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lokales Hosting widerspricht Watchdog-Konzept&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Inhalte öffentlich verfügbar&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;em&gt;ntfy&lt;/em&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Simpel und leichtgewichtig&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Registrierung optional bei Web-Nutzung&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Client zweckorientiert&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Unsicher: Keine Verschlüsselung ohne Registrierung&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Kostenlos für Kleinanwender&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Unabhängig von der eigenen Maschine&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;em&gt;Signal&lt;/em&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&amp;ldquo;Note to self&amp;rdquo; einfach einzurichten&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Einrichtung und Konfiguration der Signal-API komplex&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Unabhängig von der eigenen Maschine&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Topics etwas schwierig umzusetzen&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Nutzlos bei Absturz des Servers&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;SMS, Mail und RSS-Feed sind durch teils oben bereits erläuterte Nachteile für mich bereits raus. Bleiben also &lt;em&gt;ntfy&lt;/em&gt; und &lt;em&gt;Signal&lt;/em&gt;. &lt;em&gt;ntfy&lt;/em&gt; besticht durch seine Einfachheit: HTTP PUSH-request auf ein selbst definiertes &amp;ldquo;Topic&amp;rdquo; senden und selbiges auf dem Handy abonnieren - fertig. Es ist auch gut erweiterbar, denn durch self-hosting kann ich später bei Bedarf die Sicherheit erhöhen (Verschlüsselung) und das Sendeverhalten steuern. &lt;em&gt;Signal&lt;/em&gt; hingegen macht einen eigenen Client mit verhältnismäßig aufwändiger Konfiguration erforderlich. Außerdem müssten die Geräte Server-Handy verbunden werden, sodass nicht einfach neue Abonnenten hinzuzufügen sind. Dafür ist die Übertragung bestens Ende-zu-Ende gesichert, ich muss mich nicht registrieren und kostenlos ist es auch noch.&lt;/p&gt;&#10;&lt;p&gt;Vorerst entscheide ich mich auch hier für die weniger komplexe Lösung mit &lt;em&gt;ntfy&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h2 id="logging"&gt;Logging&lt;/h2&gt;&#10;&lt;p&gt;Gut. Nun ist geklärt, dass ich per &lt;em&gt;curl&lt;/em&gt; und &lt;em&gt;apprise&lt;/em&gt; über den &lt;em&gt;ntfy&lt;/em&gt;-Dienst nach Hause telefoniere. Nun schaue ich mal, welcher Inhalt zu übermitteln ist und wie ich möglichst unaufdringlich, dabei aber kurz und prägnant bleibe.&lt;/p&gt;&#10;&lt;h3 id="wann-sollen-logs-gesendet-werden"&gt;Wann sollen Logs gesendet werden?&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;em&gt;borgmatic&lt;/em&gt;: Wenn ein Backup fehlschlägt&lt;/li&gt;&#10;&lt;li&gt;Bei erfolgreichem Login auf meinen Server oder Gitea&lt;/li&gt;&#10;&lt;li&gt;Nach Ausrollen eines Updates der Server-Konfiguration&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;docker&lt;/em&gt;: Falls eine Applikation ausfällt oder nicht hochfährt&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;gitea&lt;/em&gt;: Fehlgeschlagener Durchlauf von &lt;em&gt;act_runner&lt;/em&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="was-gehört-für-mich-in-einen-bericht"&gt;Was gehört für mich in einen Bericht?&lt;/h3&gt;&#10;&lt;p&gt;Ich möchte die klassischen &amp;ldquo;W-Fragen&amp;rdquo; beantwortet bekommen.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Wann ist es passiert (Zeitstempel)?&lt;/li&gt;&#10;&lt;li&gt;Welche Anwendung berichtet?&lt;/li&gt;&#10;&lt;li&gt;Was ist passiert?&lt;/li&gt;&#10;&lt;li&gt;Wo (Modul, Codezeile) etc. ist das passiert?&lt;/li&gt;&#10;&lt;li&gt;Wie viele Verletzte (Schweregrad, Recovery)?&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Eine Benachrichtigung sieht dann etwa so aus:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;TIMESTAMP APPLICATION PRIORITY MESSAGE EFFECT DETAIL &#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="rohfassung-eines-berichts"&gt;Rohfassung eines Berichts&lt;/h3&gt;&#10;&lt;p&gt;Eine &lt;em&gt;ntfy&lt;/em&gt;-Nachricht könnte ungefähr so aussehen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Title: Fehler Borgmatic&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Priority: urgent&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Tags: warning&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -d &lt;span style="color:#e6db74"&gt;&amp;#34;YYYY-MM-DD HH:MM:SS Backup-Erstellung abgebrochen. Zugriff auf Repository blockiert&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ntfy.sh/schallberts-topic&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-08-20_ntfytest.jpg" alt="Image: First ntfy test on my phone app sent with curl" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich installiere die entsprechende App auf meinem Handy, registriere mich auf das &amp;ldquo;Topic&amp;rdquo; und sende die Nachricht ab. Es ist schön, wenn Dinge einfach funktionieren!&#10;Aha, die App zeigt den Zeitpunkt des Einganges an. Das ist mir genau genug.&lt;/p&gt;&#10;&lt;h2 id="durchführung"&gt;Durchführung&lt;/h2&gt;&#10;&lt;p&gt;Ich nehme mir hier nacheinander alle Dienste vor, für die ich gern Benachrichtigungen einrichten möchte.&lt;/p&gt;&#10;&lt;h3 id="borgmatic-1"&gt;&lt;em&gt;borgmatic&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;Starten wir mit einem Pilotversuch wieder in kleinen Schritten. Als Erstes konfiguriere ich Borgmatic zum Senden einer Nachricht über &lt;em&gt;Apprise&lt;/em&gt; an &lt;em&gt;ntfy&lt;/em&gt; bei Fehlschlag der Backup-Erstellung:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /borgmatic.d/config.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;on_error&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;echo &amp;#34;Error while creating a backup.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;apprise -vv --title &amp;#34;Borgmatic Error&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --&lt;span style="color:#ae81ff"&gt;body &amp;#34;Could not run {output}. Aborted {error}.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;ntfy://schallberts-topic&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dann teste ich das Kommando, indem ich innerhalb des Borgmatic-Containers &lt;em&gt;apprise&lt;/em&gt; anspreche. Und tatsächlich, es funktioniert. Aber der Weg hierhin hat mich locker eine Stunde gekostet, da die &lt;code&gt;yml&lt;/code&gt; mit ihren Syntaxregeln sogar innerhalb von Strings interpretiert und &lt;em&gt;borgmatic&lt;/em&gt; aufgrund von &lt;code&gt;:&lt;/code&gt; sowie &lt;code&gt;-&lt;/code&gt; Zeichen ständig die Konfigurationsdatei nicht lesen wollte. Wenn ihr einen Fehler sieht ähnlich diesem hier:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;At &amp;#39;on_error[1]&amp;#39;: {&amp;#39;apprise -vv --title &amp;#34;Borgmatic Error&amp;#34; --body &amp;#34;Could not run {output}&amp;#39;: &amp;#39;Aborted {error}.&amp;#34; ntfy://schallberts-topic&amp;#39;} is not of type &amp;#39;string&amp;#39;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Daran ist zu erkennen, dass die Interpretation von Zeichen oder Einrückungen schiefgelaufen ist und die Zeichensetzung zu prüfen ist. Alternativ kann auch der Pipe-Operator &lt;code&gt;|&lt;/code&gt; verwendet werden, um ein Kommando zu kombinieren. Referenz: &lt;a href="https://yaml.org/spec/1.2-old/spec.html#id2795688" target="_blank" rel="noopener noreferrer" class="external-link"&gt;yml-Spezifikation&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;h3 id="server-1"&gt;Server&lt;/h3&gt;&#10;&lt;p&gt;Eine Anwendung von &lt;em&gt;ntfy&lt;/em&gt; zur Überwachung von Logins auf einem Server findet sich bereits in der &lt;a href="https://docs.ntfy.sh/examples/#ssh-login-alerts" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Dokumentation von &lt;em&gt;ntfy&lt;/em&gt; selbst&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Die Beschreibung zeigt, dass so etwas leicht selbst umzusetzen ist und gibt unter Verwendung von Pluggable Authentication Modules (&lt;a href="https://en.wikipedia.org/wiki/Linux_PAM" target="_blank" rel="noopener noreferrer" class="external-link"&gt;PAM-Bibliothek&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) ein tolles Beispiel.&lt;/p&gt;&#10;&lt;p&gt;Am Ende der Datei &lt;code&gt;sshd&lt;/code&gt; im Verzeichnis &lt;code&gt;etc/pam.d&lt;/code&gt; folgenden Code einfügen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;session optional pam_exec.so /usr/bin/ntfy-ssh-login.sh&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Diese Zeile sagt &lt;em&gt;PAM&lt;/em&gt;, dass sie bei Öffnen einer &lt;code&gt;session&lt;/code&gt; per &lt;code&gt;ssh&lt;/code&gt; das ausführende Modul &lt;code&gt;pam_exec&lt;/code&gt; aufrufen soll, welches dann das hernach angegebene Skript abspult. Der Wert &lt;code&gt;optional&lt;/code&gt; bedeutet, dass die Konfigurationsdatei auch bei Fehlschlag der Aktion weiter durchlaufen werden soll. Mehr Details zum Umgang mit &lt;em&gt;PAM&lt;/em&gt; gibt es z.B. auf &lt;a href="https://www.baeldung.com/linux/pam-ssh-login-notifications" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Baeldung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Anschließend ist im Skript &lt;code&gt;ntfy-ssh-login.sh&lt;/code&gt; schlicht der &lt;em&gt;ntfy&lt;/em&gt;-Aufruf zu hinterlegen, wenn das Skript feststellt, dass PAM ein &lt;code&gt;open_session&lt;/code&gt; Event festgestellt hat.&lt;/p&gt;&#10;&lt;p&gt;Genau so habe ich es bei mir umgesetzt und es funktioniert auf Anhieb. Klasse! Der einzige Nachteil: Diese Modifikation habe ich direkt auf dem Server gemacht. Ohne Container und außerhalb meiner Konfigurations-Sicherung. Wenn ich den Server jetzt aus irgendeinem Grund neu aufsetzen muss, ist die Änderung im &lt;em&gt;PAM&lt;/em&gt; futsch und ich bekomme keine Benachrichtigungen mehr, bis ich die Änderung manuell erneut einpflege.&lt;/p&gt;&#10;&lt;h3 id="server-konfiguration"&gt;Server-Konfiguration&lt;/h3&gt;&#10;&lt;p&gt;Für das Senden eines Berichts nach Ausführen der Konfigurationsautomation muss ich eigentlich nur das im Rahmen des letzten Artikel verfasste &lt;a href="https://blog.schallbert.de/server-config-deploy/#die-fertige-automation"&gt;server-config-action&lt;/a&gt; Skript anpassen.&lt;/p&gt;&#10;&lt;p&gt;Zunächst möchte ich informiert werden, wenn das Skript fehlerfrei durchlief. Dafür füge ich am Ende der Datei einen &lt;em&gt;curl&lt;/em&gt;-Befehl ein.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# server-config-action.sh&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# action commands...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# send success notification&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Title: server-config-action&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Priority: low&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Tags: white_check_mark&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -d &lt;span style="color:#e6db74"&gt;&amp;#34;Rollout successful&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ntfy.sh/schallbert-server-config-push-topic&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sollte das Skript allerdings nicht fehlerfrei durchlaufen, so möchte ich folgendes tun:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Aktionen nach Auftreten des Fehlers abbrechen&lt;/li&gt;&#10;&lt;li&gt;Ein Fehler-Log erstellen&lt;/li&gt;&#10;&lt;li&gt;Dieses Log versenden&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Den ersten Punkt kann ich durch Hinzufügen einer Falle (Englisch &amp;ldquo;Trap&amp;rdquo;) für Fehler erreichen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# server-config-action.sh&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;trap &lt;span style="color:#e6db74"&gt;&amp;#39;handle_error $LINENO&amp;#39;&lt;/span&gt; ERR&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# action commands...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hiermit wird die Funktion &lt;code&gt;handle_error&lt;/code&gt; aufgerufen. Ihr wird die Zeilennummer mitgegeben, an der der Fehler &lt;code&gt;ERR&lt;/code&gt; auftrat. Damit decke ich auch Fehler ab, die beim Neustart der Container entstehen können. Das Fehler-Log erstelle ich mir durch Wegschreiben der Ausgabe der einzelnen Skriptbefehle. Dies erreiche ich mit der folgenden Zeile:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# server-config-action.sh&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;exec 3&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 1&amp;gt;server-config-action.log 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# action commands ...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Jeglicher Standard-Output &lt;code&gt;stdout&lt;/code&gt; soll mit diesem Befehl an den Dateideskriptor &lt;code&gt;3&lt;/code&gt;, hier angegeben mit der Logdatai &lt;code&gt;server-config-action-log&lt;/code&gt;, überschreibend weitergereicht werden. Für &amp;ldquo;Anhängen&amp;rdquo; müssten es zwei Umleitungsoperatoren &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt; sein. Somit ersetzt die Logdatei die Konsolenausgabe, welche ich im vorigen Artikel noch an dieser Stelle hatte.&lt;/p&gt;&#10;&lt;p&gt;Um das Log zugesandt zu bekommen, definiere ich nun folgende Funktion am Anfang des Bash-Skripts:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# server-config-action.sh&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# on error, send a notification&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;handle_error&lt;span style="color:#f92672"&gt;()&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# stop redirecting to file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exec 1&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; 1&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; curl &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Title: server-config-action FAILED&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Priority: high&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Tags: x&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -T server-config-action.log &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Filename: server-config-action.log&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ntfy.sh/schallbert-server-config-push-topic&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exit &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# set error trap&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# redirect stdout and stderr to file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# action commands...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-08-20_ntfytest_allchannels.jpg" alt="Image: Phone screenshot of my ntfy messages" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="gitea-1"&gt;&lt;em&gt;Gitea&lt;/em&gt;&lt;/h3&gt;&#10;&lt;p&gt;Für Berichte von Github Actions gibt es bereits ein existierendes Beispiel auf &lt;a href="https://docs.ntfy.sh/examples/#github-actions" target="_blank" rel="noopener noreferrer" class="external-link"&gt;ntfy_examples/#github-actions&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Und das Beste: Da der &lt;em&gt;act_runner&lt;/em&gt; von &lt;em&gt;Gitea&lt;/em&gt; an den wesentlichen Stellen &lt;a href="https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables" target="_blank" rel="noopener noreferrer" class="external-link"&gt;in den Umgebungsparametern&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; kompatibel ist, funktioniert das Senden an &lt;em&gt;ntfy&lt;/em&gt; bei mir auf Anhieb. In der Workflow-Datei des runners muss nur der auf der Website angegebene &lt;em&gt;curl&lt;/em&gt;-Befehl angegeben werden und fertig.&lt;/p&gt;&#10;&lt;h2 id="ergebnis"&gt;Ergebnis&lt;/h2&gt;&#10;&lt;p&gt;Hiermit sind jetzt fünf Benachrichtigungen über die wichtigsten Vorgänge auf meinem Server eingerichtet. Ich habe die zugrundeliegenden Mechaniken verstanden und kann jederzeit weitere Benachrichtigungen erstellen, wenn ich sie benötige. Löse ich alle Benachrichtigungen testweise aus, zeigt mein Handy das dargestellte Bild.&lt;/p&gt;&#10;&lt;p&gt;Eine Unabhängigkeit vom zu überwachenden System habe ich noch nicht erreicht. Die Benachrichtigungen gehen sämtlich von der betroffenen Maschine aus und es bestehen zum Teil &lt;a href="https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/#reverse-proxy-connection-refused"&gt;sogar Abhängigkeiten&lt;/a&gt; zwischen Docker-Containern. Der &lt;em&gt;act_runner&lt;/em&gt; muss sich zum Beispiel über Websockets mit &lt;em&gt;Gitea&lt;/em&gt; verbinden. Und das funktioniert nur, wenn &lt;em&gt;Caddy&lt;/em&gt; den Reverse Proxy bereitstellt.&lt;/p&gt;&#10;&lt;p&gt;Stelle ich in den nächsten Monaten fest, dass ein Dienst &amp;ldquo;unter meinem Radar&amp;rdquo; nicht mehr arbeitsfähig ist, werde ich diese Unabhängigkeit herstellen müssen und entsprechend darüber berichten.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Kontext zum Setup mit &lt;a href="https://docs.ntfy.sh/#getting-started" target="_blank" rel="noopener noreferrer" class="external-link"&gt;ntfy&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:2"&gt;&#10;&lt;p&gt;Kontext zur &lt;a href="https://github.com/caronc/apprise/wiki/Notify_signal" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Signal-API&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:3"&gt;&#10;&lt;p&gt;Bericht von &lt;code&gt;asad-awadia&lt;/code&gt; zur &lt;a href="https://blog.aawadia.dev/2023/04/24/signal-api/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Einrichtung der Signal-API&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:4"&gt;&#10;&lt;p&gt;Healthchecks ist direkt verfügbar als &lt;a href="https://hub.docker.com/r/healthchecks/healthchecks" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Docker-Image&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:5"&gt;&#10;&lt;p&gt;Übrigens sind die Operatoren &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; und &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; für mich nicht schwer zu merken, denn früher habe ich viel mit der Programmiersprache &lt;code&gt;C&lt;/code&gt; zu tun gehabt. Dort - und in vielen anderen Programmiersprachen auch - sind dies Shift-Operatoren, die einen Wert bitweise &amp;ldquo;verschieben&amp;rdquo; bzw. ein Feld auf ein anderes schieben können. Bei den &lt;a href="https://en.wikipedia.org/wiki/Reduced_instruction_set_computer" target="_blank" rel="noopener noreferrer" class="external-link"&gt;RISC&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Architekturen, die ich damals verwendet habe, war die Schiebe-Operation &amp;ldquo;billig&amp;rdquo;, also sehr schnell und speicherplatzschonend umgesetzt.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Server-Konfiguration ausrollen</title><link>https://blog.schallbert.de/server-config-deploy/</link><pubDate>Wed, 31 Jul 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/server-config-deploy/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-07-31-server-configdeploy-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Diagram showing a config file rollout on Schallbert&amp;#39;&amp;#39;s server"&#10; title="Server-Konfiguration ausrollen" /&gt;&#10;&lt;p&gt;Im vorigen Artikel habe ich meine &lt;a href="https://blog.schallbert.de/server-config-version-control/"&gt;Konfigurationsdateien unter Versionskontrolle gebracht&lt;/a&gt;. Nun will ich die auf dem Server bereitgestellten Updates automatisiert einspielen.&lt;/p&gt;&#10;&lt;h2 id="worum-geht-es-hier"&gt;Worum geht es hier?&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Schreiben eines Skripts zur automatischen Erkennung des Update-Triggers&lt;/li&gt;&#10;&lt;li&gt;Server-Anwendungen sollen heruntergefahren und eine Sicherungskopie erstellt werden&lt;/li&gt;&#10;&lt;li&gt;Das Skript soll die Konfiguration auf dem System verteilen&lt;/li&gt;&#10;&lt;li&gt;Anschließend sollen alle Anwendungen neu gestartet werden&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="wege-und-möglichkeiten"&gt;Wege und Möglichkeiten&lt;/h2&gt;&#10;&lt;p&gt;Auch hier habe ich wieder mehrere Stunden mit Recherche verbracht. Bei größeren Projekten verwenden Infrastrukturexperten anscheinend spezialisierte Automatisierungswerkzeuge. Dazu zählen &lt;a href="https://docs.ansible.com/ansible/latest/getting_started/index.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Ansible&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, einfachere Tools wie &lt;a href="https://www.cdi.st/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;cdist&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; oder gar &lt;a href="https://kubernetes.io/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Kubernetes&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; für stark skalierende Dienste.&lt;/p&gt;&#10;&lt;h3 id="automatisierungsdienste-ansible-cdist-kubernetes"&gt;Automatisierungsdienste Ansible, cdist, Kubernetes&lt;/h3&gt;&#10;&lt;p&gt;Ansible und cdist scheinen dabei ein ähnliches Konzept zu verfolgen: Auf der Quellmaschine (in diesem Beispiel meinem Laptop) erstelle ich die Konfiguration für meinen Server und lege diese sowie die Vorschrift zur Konfiguration meiner Dienste in einem &amp;ldquo;Playbook&amp;rdquo; (Ansible) bzw. in &amp;ldquo;types&amp;rdquo; (cdist) ab.&lt;/p&gt;&#10;&lt;p&gt;Vereinfacht gesagt - so wie ich das verstanden habe - kümmert sich das Werkzeug anschließend auf Knopfdruck darum, die Konfiguration zu bauen, sich per &lt;code&gt;ssh&lt;/code&gt; auf dem Zielhost einzuwählen, sie dort hinüberzuschieben und anschließend zu starten. Für Ansible existieren sogar für mein Szenario mit &lt;code&gt;docker-compose&lt;/code&gt; &lt;a href="https://medium.com/swlh/deploying-docker-compose-applications-with-ansible-and-github-actions-7f1740392507" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Tutorials wie dieses&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, was den Einstieg nochmal erleichtert.&lt;/p&gt;&#10;&lt;p&gt;Einen anderen Weg geht Kubernetes, was sich als eher als Containermanager, Loadbalancer und Skalierungsagent versteht, aber für meine Zwecke Ähnliches leisten kann.&lt;/p&gt;&#10;&lt;h3 id="mein-weg"&gt;Mein Weg&lt;/h3&gt;&#10;&lt;p&gt;Ich hingegen brauche von den Fähigkeiten dieser Programme nur einen Bruchteil. Zudem schrecken mich der &amp;ldquo;zusätzliche&amp;rdquo; &lt;code&gt;ssh&lt;/code&gt;-Kanal, der Konfigurationsaufwand, die teils auf dem Zielsystem erforderlichen, zusätzlichen Programme sowie das erforderliche Einlesen und Auswählen des für mich besten Werkzeuges ab. Denn dank meiner sehr einfachen Pipeline aus dem letzten Artikel befindet sich die Konfiguration bereits auf meinem Server. Sie muss &amp;ldquo;nur noch&amp;rdquo; an die richtigen Stellen kopiert und betroffene Services einem Neustart unterzogen werden.&lt;/p&gt;&#10;&lt;p&gt;Daher versuche ich, dieses Problem mit Bordwerkzeug, meinem Gehirn in Betriebsbereitschaft und ein paar Suchanfragen bei Einschlägigen Foren zu den Themen &lt;a href="https://superuser.com/questions/181517/how-to-execute-a-command-whenever-a-file-changes" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&amp;ldquo;Ausführen eines Skriptes bei Dateiänderung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://www.freecodecamp.org/news/copy-a-directory-in-linux-how-to-cp-a-folder-in-the-command-line-in-linux-and-unix-macos/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&amp;ldquo;Kopieren von Ordnerstrukturen unter Linux&amp;rdquo;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zu lösen.&lt;/p&gt;&#10;&lt;h3 id="risiko-zirkelbezug"&gt;Risiko Zirkelbezug&lt;/h3&gt;&#10;&lt;p&gt;Ein Risiko gehe ich dabei jedoch ein: Da das Deployment über Gitea läuft, die Konfiguration aber Gitea selbst betrifft, kann ich bei einem Fehler in diesem Modul nichts mehr um- oder zurückstellen: Der Gitea-Service ist dann ja kaputt. In diesem Szenario müsste ich also händisch mit auf dem Server die Konfiguration wieder ans Laufen bekommen.&lt;/p&gt;&#10;&lt;p&gt;Ich probiere es jetzt trotzdem aus und schaue, ob ich tatsächlich mit so einem Problem konfrontiert werde. Falls ja, steige ich halt auf &lt;code&gt;cdist&lt;/code&gt; um und dokumentiere dies in einem eigenen Artikel! 🤗&lt;/p&gt;&#10;&lt;h2 id="vorbereitung-ordnersystem-und-skripte-erstellen"&gt;Vorbereitung: Ordnersystem und Skripte erstellen&lt;/h2&gt;&#10;&lt;p&gt;Ich überlege kurz und erstelle ein paar Ordner im Server-Dateisystem:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /opt/server-config&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir automation-hooks-trigger&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir automation-hooks-handler&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Diese Ordner sollen die zwei Seiten der Automation abbilden. &lt;code&gt;trigger&lt;/code&gt; enthält Textdateien, die von der (Web)-Serviceseite aus manipuliert werden können. So soll &lt;em&gt;act_runner&lt;/em&gt; beispielsweise die Datei &lt;code&gt;server-config-update&lt;/code&gt; schreiben, sobald ein Update vorliegt.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;service &amp;ndash;&amp;gt; writes to trigger file ||| server_handler() &amp;ndash;&amp;gt; trigger_file.changed ? run_action() : loop()&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Im Handler-Ordner wiederum befinden sich Skriptdateien, die die erforderlichen Änderungen auf dem Server-Dateisystem vornehmen.&lt;/p&gt;&#10;&lt;h2 id="durchführung-shell-script-zum-abhandeln-des-updates"&gt;Durchführung: Shell-Script zum Abhandeln des Updates&lt;/h2&gt;&#10;&lt;p&gt;Im vorigen Artikel habe ich im Container &lt;em&gt;act_runner&lt;/em&gt; zum Signalisieren des Vorhandenseins einer neuen Serverkonfiguration das Kommando &lt;code&gt;touch server-config-update.txt&lt;/code&gt; ausgeführt. Auf dem Server prüfe ich nun mit folgendem Code periodisch ab, ob sich diese Datei geändert hat.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;### set directories&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;actionfile&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/opt/server-config/automation-hooks-handler/server-config-action.sh&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;triggerfile&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/opt/server-config/automation-hooks-trigger/server-config-update.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;### Set initial time of file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;LTIME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;stat -c %Z &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;triggerfile&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; true&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ATIME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;stat -c %Z &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;triggerfile&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$ATIME&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; !&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$LTIME&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;actionfile&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LTIME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$ATIME&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sleep &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Per &lt;a href="https://en.wikipedia.org/wiki/Shebang_%28Unix%29" target="_blank" rel="noopener noreferrer" class="external-link"&gt;#!/bin/bash&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; wird dem Betriebssystem der Interpreter genannt, mit dem das Skript auszuführen ist. Der Befehl &lt;a href="https://wiki.ubuntuusers.de/stat/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;stat -c %Z&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; fragt ab, wann die Datei zuletzt geändert wurde und gibt den Zeitpunkt im &lt;a href="https://www.epochconverter.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Epoch-Format&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zurück. Schließlich wird die zu Anfang des Skriptes mit der in der Schleife genommenen Zeit verglichen und bei Veränderung kann dann die noch zu schreibende Deploy-Routine ablaufen. Anschließend wird der Referenzzeitpunkt aktualisiert.&#10;Zum Schluss pausiert das Skript (&lt;code&gt;sleep 10&lt;/code&gt;) für zehn Sekunden, bevor die Abfrage erneut losläuft. Ich verwende absolute Pfade, da ich das Skript sowohl für Tests von der Konsole aus als auch automatisch per &lt;code&gt;cron&lt;/code&gt; oder &lt;code&gt;systemd&lt;/code&gt; starten können will.&lt;/p&gt;&#10;&lt;h3 id="erster-test-und-einhängen-in-den-autostart"&gt;Erster Test und Einhängen in den &amp;ldquo;Autostart&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Nun müssen wir die Datei für einen ersten Test ausführbar machen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# change file mode bits: add &amp;#34;executable&amp;#34; flag to server-config-handler script&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chmod +x /opt/server-config/automation-hooks-handler/server-config-handler.sh&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dann erstelle ich die Datei &lt;code&gt;server-config-action.sh&lt;/code&gt; und gebe ihr als einziges Kommando ein &lt;code&gt;echo&lt;/code&gt;. Schauen wir mal, ob bis hierhin alles funktioniert. Dafür erstelle ich lokal meine Update-Triggerdatei und starte das Skript. Anschließend modifiziere ich sie in einer anderen Shell-Instanz per &lt;code&gt;touch server-config-update.txt&lt;/code&gt; und prompt erscheint in der Konsole &lt;code&gt;--- CONFIG UPDATE TRIGGER detected ---&lt;/code&gt;. Super!&lt;/p&gt;&#10;&lt;p&gt;Auf dem Server später muss ich das Skript automatisch nach einem Neustart ausführen lassen. Dafür nehme ich das Werkzeug &lt;code&gt;cron&lt;/code&gt; zur &lt;a href="https://wiki.ubuntuusers.de/Cron/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Hilfe&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und erstelle einen neuen Eintrag per &lt;code&gt;crontab -e&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# cron can automatically execute recurring tasks&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# in this case, we&amp;#39;re running server-config-handler script on reboot&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;@reboot sh /opt/server-config/automation-hooks-handler/server-config-handler.sh&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mit dem Befehl &lt;code&gt;ps aux&lt;/code&gt; kann ich nun prüfen, ob das Skript auch wirklich ausgeführt wird:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;schallbert@server: ps aux&#10;[...]&#10;8:15 0:00 /bin/bash ./server-config-handler.sh&#10;8:15 0:00 sleep 10&#10;[...]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="sicherungskopie"&gt;Sicherungskopie&lt;/h2&gt;&#10;&lt;p&gt;Eine Sicherungskopie meiner Anwendungen und Dateien anzufertigen bevor ich das Update ausrolle macht total Sinn. Also teile ich &lt;a href="https://blog.schallbert.de/server-protection/#regelm%c3%a4%c3%9fige-backups"&gt;Borg&lt;/a&gt; mit, dass ich jetzt ein Backup anlegen möchte. Vorher muss ich natürlich alle Services stoppen. Somit sind alle Daten zugriffsfrei, kohärent und statisch.&lt;/p&gt;&#10;&lt;h3 id="zustand-und-daten-einfrieren"&gt;Zustand und Daten einfrieren&lt;/h3&gt;&#10;&lt;p&gt;Hierfür lege ich in &lt;code&gt;automation-hooks-handler&lt;/code&gt; ein Skript an, welches alle Container bis auf &lt;em&gt;borg&lt;/em&gt; selbst beendet.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! /bin/bash&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /opt/automation-hooks-handler/backup-pre-action.sh&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# this shell script shuts down all docker containers prior to backup&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;Shutting down containers for backup:&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;watchtower...&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd /opt/watchtower&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker compose down 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Der Ausdruck &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt; bedeutet, dass eventuelle Ausgabe von Fehlern in die Konsole umgeleitet wird. Die Zahl &lt;code&gt;1&lt;/code&gt; stellt nämlich den Filedescriptor für &lt;code&gt;stdout&lt;/code&gt; dar, während &lt;code&gt;2&lt;/code&gt; &lt;code&gt;stderr&lt;/code&gt; meint. Der Operator &lt;code&gt;&amp;gt;&amp;amp;&lt;/code&gt; funktioniert als &lt;code&gt;redirect merger&lt;/code&gt;, also als Umleitung und Zusammenführung. Später können wir an diesem Punkt hingehen und die Ausgabe in ein Logfile schreiben - das lasse ich aber der Einfachheit halber erst einmal aus.&lt;/p&gt;&#10;&lt;h3 id="borgmatic-trigger-handler-mechanismus-2-und-3"&gt;Borgmatic: Trigger-Handler Mechanismus #2 und #3&lt;/h3&gt;&#10;&lt;p&gt;Das Skript kann ich nun auf zwei Arten ausführen lassen:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Als Aufruf durch das weiter oben beschriebene Skript &lt;code&gt;server-config-handler&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Durch die vor &lt;em&gt;borg&lt;/em&gt; geschaltete Automatisierungslösung &lt;em&gt;borgmatic&lt;/em&gt;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Ich entscheide mich für die zweite Option und schreibe daher in die &lt;code&gt;borgmatic.d/config.yml&lt;/code&gt; die folgenden Befehle:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# borgmatic.d/config.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# List of one or more shell commands or scripts to execute before&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# creating a backup, run once per repository.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;before_backup&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;echo &amp;#34;Triggering container shutdown for backup.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;touch /etc/automation-hooks-trigger/backup-pre.txt&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;sleep 20&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;echo &amp;#34;Assuming container shutdown complete. Creating the backup now.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;after_backup&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;echo &amp;#34;Triggering container restart after backup.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;touch /etc/automation-hooks-trigger/backup-post.txt&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;sleep 10&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;echo &amp;#34;Assuming container restart complete. Exiting.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Damit dies richtig funktioniert, muss ich in der zugehörigen &lt;code&gt;docker-compose.yml&lt;/code&gt; natürlich ein Volume anlegen und auf den Pfad im Dateisystem des Servers zeigen lassen: &lt;code&gt;${VOLUME_UPDATE_TRIGGER}:/etc/automation-hooks-trigger&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Nun muss ich die Gegenseite dieser Trigger einrichten: Handler überwachen die Triggerdateien auf Änderungen und rufen entsprechend Action-Skripte auf. Sie sind bis auf die Pfade von &lt;code&gt;triggerfile&lt;/code&gt; und &lt;code&gt;actionfile&lt;/code&gt; identisch mit dem oben erläuterten Handler.&lt;/p&gt;&#10;&lt;h3 id="das-backup-anlegen"&gt;Das Backup anlegen&lt;/h3&gt;&#10;&lt;p&gt;Würde ich &lt;em&gt;borg&lt;/em&gt; direkt ansprechen, so ließe sich das Backup über &lt;code&gt;create&lt;/code&gt; erstellen. Dazu müsste ich angeben, in welchem Repository die Sicherungskopie abgelegt werden soll und unter welchem Namen. Im Beispiel unten wird das mit dem Scope-Operator vorgegeben: &lt;code&gt;::config-update&lt;/code&gt;. Anschließend werden die zu sichernden Ordner angegeben.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;borg create /path/to/repo::config-update ~/opt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ich verwende ja &lt;em&gt;borgmatic&lt;/em&gt;, was mir über die Konfigurationsdatei einiges abnimmt. Dafür muss ich den Befehl allerdings im Container ausführen. Um besser prüfen zu können, ob auch alles funktioniert, lasse ich mir Statistiken &amp;ldquo;verbose&amp;rdquo; auf die Konsole ausgeben (&lt;code&gt;--stats -v 1&lt;/code&gt;) sowie die kopierten Dateien anzeigen &lt;code&gt;--files&lt;/code&gt;.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec borgmatic sh -c &lt;span style="color:#e6db74"&gt;&amp;#34;cd &amp;amp;&amp;amp; borgmatic --stats -v 1 --files 2&amp;gt;&amp;amp;1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Diese Zeile füge ich dem Automationsskript hinzu.&lt;/p&gt;&#10;&lt;h2 id="zweiter-test-zum-anlegen-des-backups"&gt;Zweiter Test zum Anlegen des Backups&lt;/h2&gt;&#10;&lt;p&gt;Wenn jetzt alles funktioniert, sieht der komplette Ablauf wie folgt aus:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Nach Eingang der geänderten Dateien durch Giteas Automation legt &lt;em&gt;act_runner&lt;/em&gt; (im Docker-Container) die Dateien auf dem Server ab und setzt anschließend den &lt;code&gt;server-config-update&lt;/code&gt; Trigger.&lt;/li&gt;&#10;&lt;li&gt;Binnen &lt;code&gt;10sec&lt;/code&gt; wird der Trigger von &lt;code&gt;server-config-handler.sh&lt;/code&gt; erkannt, welches anschließend &lt;code&gt;server-config-action.sh&lt;/code&gt; aufruft.&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;borgmatic&lt;/em&gt; (im Docker-Container) wird darin beauftragt, ein Backup anzufertigen. Dieses wiederum schreibt den &lt;code&gt;pre-backup&lt;/code&gt; Trigger.&lt;/li&gt;&#10;&lt;li&gt;Erneut binnen &lt;code&gt;10sec&lt;/code&gt; wird durch diesen &lt;code&gt;pre-backup-handler.sh&lt;/code&gt; das &lt;code&gt;backup-pre-action.sh&lt;/code&gt; Skript aufgerufen und alle Container bis auf &lt;em&gt;borgmatic&lt;/em&gt; gestoppt.&lt;/li&gt;&#10;&lt;li&gt;Durch die eingebaute Verzögerung wartet &lt;em&gt;borgmatic&lt;/em&gt; dies ab und erstellt nun das Backup.&lt;/li&gt;&#10;&lt;li&gt;Nach dem Backup schreibt &lt;em&gt;borgmatic&lt;/em&gt; den &lt;code&gt;backup-post-action.sh&lt;/code&gt; Trigger.&lt;/li&gt;&#10;&lt;li&gt;Binnen weiterer &lt;code&gt;10sec&lt;/code&gt; erkennt &lt;code&gt;post-backup-handler.sh&lt;/code&gt; die geänderte Datei und fährt alle Container über &lt;code&gt;post-backup-actions.sh&lt;/code&gt; wieder hoch.&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;borgmatic&lt;/em&gt; teilt &lt;code&gt;server-config-update.sh&lt;/code&gt; mit, ob irgendwo im Prozess bis jetzt Fehler auftraten. Falls nicht, wird fortgefahren.&lt;/li&gt;&#10;&lt;li&gt;Alle Docker-Container werden gestoppt.&lt;/li&gt;&#10;&lt;li&gt;Die Server-Konfiguration wird auf die entsprechenden Stellen ausgerollt.&lt;/li&gt;&#10;&lt;li&gt;Es findet ein Neustart aller Docker-Container mit neuer Konfiguration statt.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Ein erster Erfolg: Bis Punkt 6 laufen diese Skripte sogar auf meinem Laptop bereits durch:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;schallbert@laptop: touch server-config-update.txt&#10;server-config-handler@laptop: --- RUN backup ---&#10;borgmatic@docker: /etc/borgmatic.d/config.yml: Running 4 commands for pre-backup hook&#10; Triggering container shutdown for backup. &#10;backup-pre-handler.sh@laptop: --- RUN backup-pre-action.sh ---&#10; Shutting down containers for backup:&#10; watchtower...&#10; [...]&#10; complete.&#10;borgmatic@docker: Assuming container shutdown complete. Creating the backup now.&#10; local: Creating archive&#10; Failed to create/acquire the lock /mnt/repository/lock.exclusive&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="fehlerbehebung-für-failed-to-acquire-the-lock"&gt;Fehlerbehebung für &amp;ldquo;failed to acquire the lock&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Dieses Problem taucht bei mir dann auf, wenn &lt;em&gt;borg&lt;/em&gt; beim Erstellen eines Backups einen Fehler meldet, der das Programm zum Abbruch bringt. In diesem Falle wird das Repository anscheinend nicht korrekt freigegeben, sodass es nach Neustart des Containers für den alten, nun nicht mehr existierenden Container reserviert bleibt. Das folgende Kommando löst dieses Problem:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec borgmatic sh -c &lt;span style="color:#e6db74"&gt;&amp;#34;cd &amp;amp;&amp;amp; borg break-lock /mnt/repository&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="übertragen-der-konfiguration"&gt;Übertragen der Konfiguration&lt;/h3&gt;&#10;&lt;p&gt;Nun müssen die Konfigurationsdateien noch an die jeweils richtige Stelle auf dem Server kopiert werden. Glücklicherweise hatte ich die Ziel-Ordnerstruktur bereits beim Erstellen des Repository geklont, sodass ich das mit einem einzigen Kopierbefehl ohne &amp;ldquo;hardcoding&amp;rdquo; hinbekommen sollte. Nach ein paar Netzrecherchen und einem Blick in die Bedienungsanleitung für den Kopierbefehl &lt;code&gt;man cp&lt;/code&gt; habe ich mein Kommando:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# copy recursively contents of folder &amp;#34;server-config&amp;#34; to &amp;#34;/opt&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo cp -r -v /opt/server-config/. /opt 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Somit sage ich dem Betriebssystem, es solle den Inhalt (&lt;code&gt;/.&lt;/code&gt;) des Ordners &lt;code&gt;server-config&lt;/code&gt; rekursiv (&lt;code&gt;-r&lt;/code&gt;), also inklusive aller Unterordner, in den Ordner &lt;code&gt;opt&lt;/code&gt; befördern, welcher sich im Stammverzeichnis &lt;code&gt;/&lt;/code&gt; befindet. &lt;code&gt;cp&lt;/code&gt; arbeitet hierbei überschreibend-ergänzend, wird also noch nicht vorhandene Dateien erstellen und vorhandene überschreiben, sie also nicht unter gleichem Namen &amp;ldquo;daneben kopieren&amp;rdquo;. Mit der Option &lt;code&gt;-v&lt;/code&gt; lasse ich zusätzliche Details ausgeben, und mit &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt; leite ich die Fehlerausgabe in die Konsole um.&lt;/p&gt;&#10;&lt;p&gt;Alle Ordnerindikatoren müssen sich genau dort befinden, wo sie im Befehl stehen: Ein Slash hinter &lt;code&gt;/opt/&lt;/code&gt; würde Ordner nicht-überschreibend redundant kopieren, Dateien aber überschreiben. Ohne &lt;code&gt;.&lt;/code&gt; würde der Ordner &lt;code&gt;server-config&lt;/code&gt; am Zielpfad erstellt.&lt;/p&gt;&#10;&lt;h3 id="umstieg-auf-rsync"&gt;Umstieg auf rsync&lt;/h3&gt;&#10;&lt;p&gt;Der &lt;code&gt;cp&lt;/code&gt;-Befehl kopiert leider auch ein paar Dateien, die ich gar nicht kopiert haben möchte: Repository-spezifische Ordner wie &lt;code&gt;.gitea&lt;/code&gt;, oder auch die Ordner für Trigger und Handler. Diese benötige ich nur unter &lt;code&gt;server-config&lt;/code&gt;, nicht aber auch direkt in &lt;code&gt;opt&lt;/code&gt;. Um dies zu beheben nutze ich stattdessen das Kommando &lt;code&gt;rsync&lt;/code&gt;. Dort kann ich mit &lt;code&gt;-u&lt;/code&gt; einstellen, dass nur neuere Dateien überschrieben werden sollen und über eine &lt;code&gt;--exclude&lt;/code&gt;-Option nicht zu kopierende Dateien und Ordner ausschließen. Dies sieht dann so aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rsync -r -u -v --exclude &lt;span style="color:#e6db74"&gt;&amp;#39;.*&amp;#39;&lt;/span&gt; --exclude &lt;span style="color:#e6db74"&gt;&amp;#39;README.md&amp;#39;&lt;/span&gt; --exclude &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;otherFolders&amp;gt;&amp;#39;&lt;/span&gt; /opt/server-config/. /opt 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Doch plötzlich startet &lt;em&gt;gitea&lt;/em&gt; nicht mehr. Fehlermeldung:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;docker@server: [...] failed to load config file &amp;#34;app.ini&amp;#34;: open: permission denied&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nach längerem Grübeln und diversen Neustarts des Docker-Clients sehe ich, dass &lt;em&gt;rsync&lt;/em&gt; die Berechtigungen der Quelldatei auf die Zieldatei geschrieben hat, was bei &lt;em&gt;cp&lt;/em&gt; vorher nicht der Fall war: &lt;code&gt;-rw-------&lt;/code&gt;. Nun ändere ich dies, indem ich &lt;code&gt;chmod +r app.ini&lt;/code&gt; ausführe. Schon fährt alles wieder wie gewohnt hoch! 🎉&lt;/p&gt;&#10;&lt;h3 id="alle-anwendungen-neu-starten"&gt;Alle Anwendungen neu starten&lt;/h3&gt;&#10;&lt;p&gt;Da ich alles auf meinem Server in Docker betreibe, genügen hierfür zwei einfache &lt;a href="https://docs.docker.com/reference/cli/docker/container/restart/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Befehle&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker stop &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;docker ps -a -q&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...roll out config changes...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker restart &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;docker ps -a -q&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="die-fertige-automation"&gt;Die fertige Automation&lt;/h2&gt;&#10;&lt;p&gt;Mein Skript ist nun fertig und ruft lediglich das Action-Skript auf.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! /bin/bash&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#/opt/automation-hooks-handler/server-config-handler.sh&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#[...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$ATIME&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; !&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$LTIME&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;--- CONFIG UPDATE TRIGGER detected ---&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ./server-config-action.sh 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LTIME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$ATIME&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sleep &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Das Skript &lt;code&gt;server-config-action&lt;/code&gt; führt dann die oben beschriebenen Aktionen aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! /bin/bash&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#/opt/automation-hooks-handler/server-config-action.sh&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;--- RUN backup ---&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec borgmatic sh -c &lt;span style="color:#e6db74"&gt;&amp;#34;cd &amp;amp;&amp;amp; borgmatic --stats -v 1 --files 2&amp;gt;&amp;amp;1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;--- STOP all containers ---&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker stop &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;docker ps -a -q&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;--- DEPLOY config ---&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cp -r -v /opt/server-config/. /opt 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;--- RESTART all containers ---&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker restart &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;docker ps -a -q&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Damit das Ganze nun zuverlässig funktioniert, müssen die drei Handler-Skripte im Hintergrund laufen:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;server-config-handler.sh&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;backup-pre-handler.sh&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;backup-post-handler.sh&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Sie reagieren auf die jeweiligen Trigger, ausgelöst durch &lt;em&gt;act_runner&lt;/em&gt; von Gitea bzw. durch &lt;em&gt;borgmatic&lt;/em&gt;. Ich erweitere das &lt;em&gt;crontab&lt;/em&gt; jetzt entsprechend und bin dann mit der Aufgabe vorerst fertig. Die Konsolenausgabe des gesamten Vorgangs sieht so aus:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;--- RUN backup ---&#10;/etc/borgmatic.d/config.yml: Running 4 commands for pre-backup hook&#10;Triggering container shutdown for backup.&#10;--- RUN backup-pre-action.sh ---&#10;Shutting down containers for backup:&#10;gitea... &#10;fail2ban...&#10;complete.&#10;Assuming container shutdown complete. Creating the backup now.&#10;local: Creating archive&#10;&amp;lt;borg archive stats&amp;gt;&#10;/etc/borgmatic.d/config.yml: Running 4 commands for post-backup hook&#10;Triggering container restart after backup.&#10;--- RUN backup-post-action.sh ---&#10;Restarting containers after backup:&#10;fail2ban...&#10;gitea... &#10;watchtower...&#10;complete.&#10;Assuming container restart complete. Exiting.&#10;local: Pruning archives&#10;local: Compacting segments&#10;compaction freed about 1.82 MB repository space.&#10;local: Running consistency checks&#10;summary:&#10;/etc/borgmatic.d/config.yml: Successfully ran configuration file&#10;--- STOP all containers ---&#10;&amp;lt;container ids&amp;gt;&#10;--- DEPLOY config ---&#10;sending incremental file list&#10;&amp;lt;files that are copied&amp;gt;&#10;sent 206,558 bytes received 3,463 bytes 420,042.00 bytes/sec&#10;total size is 193,167 speedup is 0.92&#10;--- RESTART all containers ---&#10;&amp;lt;container ids&amp;gt;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Prima! Nun fehlt mir nur noch, dass diese Ausgabe mir zugestellt wird, falls etwas bei diesem Vorgang schief geht.&lt;/p&gt;&#10;</description></item><item><title>Server-Konfiguration mit Git</title><link>https://blog.schallbert.de/server-config-version-control/</link><pubDate>Mon, 15 Jul 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/server-config-version-control/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-07-15-server-versioncontrol-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Diagram thumb for putting server config under version control"&#10; title="Server-Konfiguration mit Git" /&gt;&#10;&lt;h2 id="worum-geht-es"&gt;Worum geht es?&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Ich wünsche eine Versionskontrolle für die Konfiguration von Serverapplikationen&lt;/li&gt;&#10;&lt;li&gt;Diskussion technischer Lösungen zur Umsetzung&lt;/li&gt;&#10;&lt;li&gt;Tutorial: Docker/Gitea/andere &amp;ldquo;Secrets&amp;rdquo; von der Versionskontrolle ausnehmen&lt;/li&gt;&#10;&lt;li&gt;Tutorial: Deploy-Pipeline erstellen&lt;/li&gt;&#10;&lt;li&gt;Automationstrigger für nachgelagertes Ausrollen der Dateien auf dem Server programmieren&lt;/li&gt;&#10;&lt;li&gt;Nächster Artikel: Automation auf dem Server integrieren und Upgrades einspielen&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="hintergrund"&gt;Hintergrund&lt;/h2&gt;&#10;&lt;p&gt;Ich habe auf &lt;a href="https://blog.schallbert.de/projects/move-blog-to-own-server/"&gt;meinem Server&lt;/a&gt; ja &lt;a href="https://blog.schallbert.de/server-auto-upgrade/"&gt;Watchtower laufen&lt;/a&gt;, um die Installierten Distributionen automatisch auf Stand und frisch gepatcht zu halten. Vor Kurzem hatte ich nun den Fall, dass sich mein &lt;a href="https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/"&gt;Gitea Action Runner&lt;/a&gt; nicht mehr automatisch starten lassen wollte und auch bei manuellem Start eine Fehlermeldung ausgab.&lt;/p&gt;&#10;&lt;p&gt;Anscheinend hatte sich Docker ohne meine Aufsicht &lt;a href="https://blog.schallbert.de/server-auto-upgrade/"&gt;auf Stand gebracht&lt;/a&gt; und warf nun beim Hochfahren des Runner-Containers einen Volumefehler, die ich so vorher noch nicht gesehen hatte. Die Fehlermeldung war eindeutig und ließ sich mittels kleiner Änderungen in einer Konfigurationsdatei für Gitea leicht beheben. Dennoch hatte ich nun das Gefühl, dass eine Versionskontrolle für meine Konfiguration sinnvoll wäre, um Änderungen, Updates und ihre Beweggründe später besser nachvollziehen zu können.&lt;/p&gt;&#10;&lt;h2 id="vorüberlegungen"&gt;Vorüberlegungen&lt;/h2&gt;&#10;&lt;p&gt;Klingt nun für mich nach einem Zirkelbezug: Ich nehme die Konfigurationsdateien für meinen Server in Gitea auf, welches selbst auf meinem Server läuft. Das könnte beim Auto-Deploy noch spannend werden. Aber mehr dazu später.&lt;/p&gt;&#10;&lt;p&gt;Spontan fallen mir zwei Möglichkeiten ein, eine Versionskontrolle mit automatischer Synchronisation zu bekommen:&lt;/p&gt;&#10;&lt;h3 id="1-hardlink"&gt;1. Hardlink&lt;/h3&gt;&#10;&lt;p&gt;Diese Lösung würde die auf meinem Server in vielen Ordnern verstreuten Konfigurationdateien per &lt;a href="https://en.wikipedia.org/wiki/Hard_link" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Hardlink&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; in einen zum Repository erklärten Ordner ablegen. Warum per Hardlink? Weil jeder Service auf meinem Server in sich gekapselt läuft und dafür jeweils eigene Konfigurationsdateien und Umgebungsvariablen gemeinsam mit dem Service abgelegt bekommt. Ohne Hardlinks müsste ich den gesamten Service-Ordner versionieren und meine &lt;code&gt;.gitignore&lt;/code&gt; entsprechend komplex aufblasen.&lt;/p&gt;&#10;&lt;p&gt;Mit Git würde ich die per Hardlink abgebildeten Dateien versionieren und ihre Inhalte auf diese Weise auf Gitea verfügbar machen.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&lt;code&gt;schallbert@server:/server-config-files&lt;/code&gt; &amp;ndash;&amp;gt; hardlinks &amp;ndash;&amp;gt; repository &amp;ndash;&amp;gt; Gitea&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Ich bekäme also eine nachgelagerte &amp;ldquo;Versionsbeobachtung&amp;rdquo; mit Sicherungskopie in dem Sinne, dass die Dateien nun mehrfach herumliegen.&lt;/p&gt;&#10;&lt;h3 id="2-repo-und-auto-deploy-auf-den-server"&gt;2. Repo und auto-deploy auf den Server&lt;/h3&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-07-15-server-versioncontrol.jpg" alt="Image: Diagram how version control with auto-deploy might work on my server" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;In diesem Szenario halte ich die Konfigurationsdateien lokal auf meinem Laptop und kann sie wie üblich mit Git versionieren. Auf Gitea würde ich das Repository abbilden, und am Ende der Kette müsste mein Runner bei jeder Änderung der Konfiguration ein Auto-Deploy auf dem Server ausführen und die betroffenen Container anschließend neu starten.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&lt;code&gt;schallbert@laptop:/server-config-files&lt;/code&gt; &amp;ndash;&amp;gt; repository &amp;ndash;&amp;gt; Gitea &amp;ndash;&amp;gt; act-runner &amp;ndash;&amp;gt; &lt;code&gt;server:/&amp;lt;service1...ServiceN&amp;gt;/config-files&lt;/code&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Ich bekäme in diesem Falle den Verwaltungs-&amp;ldquo;Main&amp;rdquo; auf meinen Laptop, und der Server würde der Abbildung auf Gitea folgen.&lt;/p&gt;&#10;&lt;h3 id="probleme"&gt;Probleme&lt;/h3&gt;&#10;&lt;p&gt;In beiden Fällen habe ich noch keine Möglichkeit, geänderte Konfigurationen vorab zu testen. Alles, was ich tue geht direkt nach &amp;ldquo;Prod&amp;rdquo; und wäre live. Schlimmstenfalls kann ich mir so recht leicht mein Setup zerschießen.&lt;/p&gt;&#10;&lt;p&gt;Ändert nun den &lt;em&gt;Status Quo ante&lt;/em&gt; nicht, bei dem ich die Dateien direkt auf dem Server verändere. Also ein neues Problem nur in der Hinsicht, dass ich beim Aufsetzen damals noch keinen Prod-Betrieb hatte und so kein Ausfallrisiko bestand.&lt;/p&gt;&#10;&lt;p&gt;Lösung Zwei scheint mir komplexer umzusetzen zu sein, denn ich benötige hierfür eine Deployment-Pipeline, welche die Dateien im Repository auf meinem Server ausrollt. Zumal sich der Runner in einem Docker-Container aufhält, während die Konfigurationsdateien direkt im Dateisystem des Servers befinden.&lt;/p&gt;&#10;&lt;p&gt;Ein direkter Durchgriff auf das Serverdateisystem &lt;a href="https://maze88.dev/docker-socket-from-within-containers.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;ist zwar technisch möglich&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, würde aber meinem beschränkten Verständnis nach &lt;a href="https://dev.to/pbnj/docker-security-best-practices-45ih" target="_blank" rel="noopener noreferrer" class="external-link"&gt;eine große Angriffsfläche für alle meine Dienste&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; bedeuten, deren Konfigurationsdateien nun über das Repository in Gitea direkt manipulierbar würden.&lt;/p&gt;&#10;&lt;h3 id="auswahl-meiner-lösung"&gt;Auswahl meiner Lösung&lt;/h3&gt;&#10;&lt;p&gt;Lösung Zwei lagert mir das Sofort-Live-Problem zumindest auf meinen Laptop vor, sodass ich in einem ersten Schritt nicht direkt am Prod-System herumfuhrwerken muss. Hier ließe sich, denke ich, leichter eine Integrationsumgebung aufsetzen, mit der ich meine Konfigurationsänderungen vorab prüfen kann. Da ich alle meine Dienste im Docker laufen habe, könnte sich das vielleicht ganz bequem lösen lassen.&lt;/p&gt;&#10;&lt;h2 id="frisch-ans-werk"&gt;Frisch ans Werk&lt;/h2&gt;&#10;&lt;h3 id="konfigurations-repo-anlegen"&gt;Konfigurations-Repo anlegen&lt;/h3&gt;&#10;&lt;p&gt;Gut, dann ziehe ich mir in einem ersten Schritt die Konfigurationsdateien vom Server. Dafür nutze ich das Dateitransferkommando &lt;a href="https://manpages.debian.org/bookworm/openssh-client/scp.1.en.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;scp&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;: &lt;code&gt;scp server:/path/to/source path/to/target&lt;/code&gt; etwa ein Dutzend mal, bis ich alle Konfigurationsdateien auf meinem Server erwischt habe.&lt;/p&gt;&#10;&lt;p&gt;Ich setze die Ordnerstruktur in diesem Repo direkt so auf, wie die Dateien auf dem Server liegen. So, hoffe ich, mache ich mir das Leben später etwas leichter.&lt;/p&gt;&#10;&lt;h3 id="secrets-in-docker-composeyml"&gt;Secrets in &lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;Nur - was tue ich mit &amp;ldquo;secrets&amp;rdquo; in den Konfigurationsdateien? Also privaten Schlüsseln, registration tokens, hashes? Die möchte ich lieber nicht mehr oder minder offen im Repository herumliegen haben. Bei Verwendung von &lt;a href="https://docs.docker.com/compose/compose-file/05-services/#env_file" target="_blank" rel="noopener noreferrer" class="external-link"&gt;docker-compose&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist das das recht einfach: Ich kann geheime Werte in versteckte Dateien für Umgebungsvariablen auslagern und vom Git-Tracking ausnehmen. Im einfachsten Falle heißen solche Dateien schlicht &lt;code&gt;.env&lt;/code&gt; und beinhalten eine Aufzählung an Umgebungsvariablen im Stil von&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BORG_PASSPHRASE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;redacted&amp;gt;&amp;#34;&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In der entsprechenden &lt;code&gt;docker-compose.yml&lt;/code&gt; ziehe ich die Variable aus der Datei &lt;code&gt;.env&lt;/code&gt; wie folgt an:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- BORG_PASSPHRASE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;BORG_PASSPHRASE&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Diese Änderungen nehme ich direkt mal lokal auf meinem Laptop vor. Über die &lt;code&gt;.gitignore&lt;/code&gt; teile ich mittels &lt;code&gt;.*&lt;/code&gt; mit, dass versteckte Dateien und damit &lt;code&gt;.env&lt;/code&gt; nicht ins Repository aufgenommen werden sollen. Doch woher weiß ich nun, ob die Container noch korrekt hochfahren?&lt;/p&gt;&#10;&lt;h3 id="secrets-in-giteas-appini"&gt;Secrets in Giteas &lt;code&gt;app.ini&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;Bei Gitea habe ich mich schon deutlich schwerer damit getan, Secrets in Dateien auszulagern. Die &lt;code&gt;app.ini&lt;/code&gt; wird auch von Gitea dynamisch geschrieben, sodass die Datei bei jedem Neustart des Services ein wenig anders aussieht. Nach langer Recherche fand ich&#10;&lt;a href="https://github.com/go-gitea/gitea/issues/25034" target="_blank" rel="noopener noreferrer" class="external-link"&gt;dieses Issue&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, in welchem eine Lösung für das separate Speichern von Secrets gesucht und gefunden wurde.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Sinnvoll erscheint mir nur das separate Speichern der Werte &lt;code&gt;INTERNAL_TOKEN&lt;/code&gt; sowie &lt;code&gt;SECRET_KEY&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Die anderen beiden &lt;a href="https://docs.gitea.com/next/administration/config-cheat-sheet/#server-server" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Properties&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; &lt;code&gt;LFS_JWT_SECRET&lt;/code&gt; und &lt;code&gt;JWT_SECRET&lt;/code&gt; werden ohnehin automatisch generiert und regelmäßig überschrieben.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;In meiner Konfiguration verwende ich lediglich &lt;code&gt;INTERNAL_TOKEN&lt;/code&gt;. Also werde jenes im Klartext und ohne Anführungszeichen in eine versteckte Datei kopieren (&lt;code&gt;.INTERNAL_TOKEN&lt;/code&gt;) und sie per Docker-Volume dem Container zur Verfügung stellen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Gitea&amp;#39;s docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./.INTERNAL_TOKEN:/run/secrets/INTERNAL_TOKEN:ro&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In der &lt;code&gt;app.ini&lt;/code&gt; ist es nun wichtig, den in der Composedatei angegebenen Pfad einzusetzen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Gitea&amp;#39;s app.ini&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;INTERNAL_TOKEN = /run/secrets/INTERNAL_TOKEN&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nicht etwa wie im oben verlinkten Issue angegeben &lt;code&gt;INTERNAL_TOKEN_URI=/run/secrets/INTERNAL_TOKEN&lt;/code&gt;, denn dies erzeugt in der von mir momentan verwendeten &lt;code&gt;V1.22.1&lt;/code&gt; einen Fehler: &lt;code&gt;Unsupported URI-Scheme&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="mal-ganz-grob-testen"&gt;Mal ganz grob testen&lt;/h3&gt;&#10;&lt;p&gt;Um zu prüfen, ob die geänderten Konfigurationsdateien noch funktionieren, installiere ich mir &lt;code&gt;docker&lt;/code&gt; und &lt;code&gt;docker-compose&lt;/code&gt; auf meinem Laptop. Anschließend probiere ich, die Container mal hochzufahren.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;# docker console log&#10;Error: Network &amp;#39;caddy-proxy&amp;#39; declared as external, but could not be found.&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Stimmt ja, Docker ist hier noch gar nicht konfiguriert. Also das Netzwerk erstellen: &lt;code&gt;sudo docker network create caddy-proxy&lt;/code&gt; und erneut versuchen. Schon beginnt der Download von Gitea samt Abhängigkeiten und der Container startet - wenn auch nicht so wie ich mir das vorgestellt habe: Innerhalb von Docker stimmen die Ordnerberechtigungen nicht, sodass weder Gitea, noch Act-runner auf alle benötigten Dateien zugreifen kann.&lt;/p&gt;&#10;&lt;p&gt;Dennoch, einen ersten Fehler finde ich:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;# gitea container log&#10;WARNING: The GITEA_RUNNER_REGISTRATION_TOKEN variable is not set. Defaulting to a blank string.&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ich hatte nämlich vergessen, den Token-String in Anführungszeichen zu setzen.&lt;/p&gt;&#10;&lt;p&gt;Für eine voll funktionsfähige Integrationsumgebung musste ich also noch mindestens zwei weitere Probleme lösen:&#10;Ordnerberechtigungen für den &lt;code&gt;Main&lt;/code&gt; auf meinem Laptop müssen so angelegt sein, dass auch der User &lt;code&gt;Docker&lt;/code&gt; Schreibrechte bekommt. Als einfache Lösung genügt vorerst, den betreffenden Volumes ein &lt;code&gt;:Z&lt;/code&gt; anzuhängen und sie so als &lt;a href="https://docs.docker.com/reference/cli/docker/container/run/#volumes-from" target="_blank" rel="noopener noreferrer" class="external-link"&gt;private unshared&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; kennzuzeichnen. Eine Volumendefinition sieht in meiner &lt;code&gt;docker-compose.yml&lt;/code&gt; nun so aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# gitea/docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./gitea:/data:Z&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ich benötige eine zweite Datei für Umgebungsvariablen, um meine Dienste auf &lt;code&gt;localhost&lt;/code&gt; umzubiegen. Immerhin startet der Service auch so und ich sehe die Logausgabe. Die meisten Konfigurationsfehler kann ich bereits jetzt entdecken.&lt;/p&gt;&#10;&lt;p&gt;Ich bin mir nicht sicher, aber möglicherweise bekomme ich zusätzliche Probleme mit dem &lt;code&gt;caddyserver&lt;/code&gt; wie Zertifikatsverwaltung, Proxy-Einstellungen und so weiter.&lt;/p&gt;&#10;&lt;h2 id="eine-deploy-pipeline-erstellen"&gt;Eine Deploy-Pipeline erstellen&lt;/h2&gt;&#10;&lt;p&gt;Jetzt wäre es noch super, wenn die in das Gitea-Repo hochgeladenen (und vorab lokal auf Funktion getesteten) Dateien automatisch ihren Weg auf meinen Server finden würden. Hierfür könnte ich ein weiteres Docker-Volume anlegen, wo &lt;code&gt;act-runner&lt;/code&gt; dann die per &lt;code&gt;on:push&lt;/code&gt; Trigger abgelegten Daten hinpackt. Sie würden dann auf meinem Server verfügbar werden.&lt;/p&gt;&#10;&lt;h3 id="setup"&gt;Setup&lt;/h3&gt;&#10;&lt;p&gt;Erinnern wir uns an &lt;a href="https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/#action-volumes"&gt;meine letzten Versuche&lt;/a&gt; mittels &lt;code&gt;act_runner&lt;/code&gt; Artefakte auf dem Server bereit zu stellen, so können wir einen Großteil dessen auch für diese Aufgabe übernehmen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# deploy-to-server.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Workflow for saving the server&amp;#39;s config repo to the local disk system&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Upload-server-config&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;run-name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${{ gitea.actor }} uploads server configuration files&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;on&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;push&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;branches&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;main&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Deploy job&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;build&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runs-on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ubuntu-latest&lt;/span&gt; &lt;span style="color:#75715e"&gt;# this is the &amp;#34;label&amp;#34; the runner will use and map to docker target OS&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;container&lt;/span&gt;: &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;: &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# left: where the output will end up on disk, right: volume name inside container&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/opt/server-config:/workspace/schallbert/server-config/tmp&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;CHECKOUT ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;: &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;path&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;./tmp&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;RUN FILE CHANGE TRIGGER ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; cd ./tmp/automation-hooks-trigger&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; touch server-config-update.txt&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="merkwürdige-volumefehler"&gt;Merkwürdige Volumefehler&lt;/h3&gt;&#10;&lt;p&gt;Doch der Weg bis hierhin war steinig. Lange hatte ich unter &lt;code&gt;volumes:&lt;/code&gt; auf der Containerseite lediglich &lt;code&gt;/server-config&lt;/code&gt; angegeben und nicht das Arbeitsverzeichnis des Runners. Dann läuft die Action zwar durch und alle Befehle der &lt;code&gt;#TEST&lt;/code&gt;-Sektion funktionieren auch. Wenn ich aber auf meinem Server nachschaue, bleibt der von Docker angelegte &lt;code&gt;server-config&lt;/code&gt; Ordner leer.&lt;/p&gt;&#10;&lt;p&gt;Mit folgenem Debug-Code unter &lt;code&gt;RUN FILE CHANGE TRIGGER&lt;/code&gt; helfe ich mir, die Pfadfehler zu finden:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# deploy-to-server.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;run&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; echo &amp;#34;hello world&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; touch updated.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; pwd&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; ls -al&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mit &lt;code&gt;echo&lt;/code&gt; prüfe ich, ob mein Code im Runner überhaupt ausgeführt wird. Das &lt;code&gt;touch&lt;/code&gt;-Kommando setzt mir den aktuellen Zeitstempel in die &lt;code&gt;updated.txt&lt;/code&gt;-Datei, sodass ich dies später als &amp;ldquo;Hook&amp;rdquo; für weitere Automation verwenden kann. &lt;code&gt;pwd&lt;/code&gt; zeigt mir den aktiven Pfad innerhalb des Runners an, sodass ich das Docker-Volume korrekt auf die Server-Festplatte abbilden kann. &lt;code&gt;ls -al&lt;/code&gt; zeigt mir, ob die im Schritt &lt;code&gt;CHECKOUT&lt;/code&gt; zusammengestellten Konfigurationsdateien korrekt geschrieben wurden.&lt;/p&gt;&#10;&lt;p&gt;Ich erkenne dadurch, dass der Volume-Pfad auf der &amp;ldquo;rechten Seite&amp;rdquo; falsch war. Ich biege ihn entsprechend auf das aktive Verzeichnis des Runners um:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# deploy-to-server.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# left: where the output will end up on disk, right: volume name inside container&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/opt/server-config:/workspace/schallbert/server-config/&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Anschließend bekam ich Folgendes zu lesen:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;# gitea / act-runner console log&#10;failed to create container: &amp;#39;Error response from daemon: Duplicate mount point: /workspace/schallbert/server-config&amp;#39;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Anscheinend legt sich der Runner die &amp;ldquo;rechte Seite&amp;rdquo; des Mountpoints automatisch selbst an und kann daher nicht erneut belegt werden. Erst durch das Hinzufügen eines weiteren Pfadteiles, in meinem Falle &lt;code&gt;/tmp&lt;/code&gt; - siehe &lt;a href="https://blog.schallbert.de/server-config-version-control/#setup"&gt;oben&lt;/a&gt; - behebe ich den Fehler und auf meinem Server erscheint endlich 😌 der lang ersehnte Ordner &lt;code&gt;server-config&lt;/code&gt; mit Inhalt:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;schallbert@schallbert-ubuntu-:/opt/server-config# ls -al&#10;total 52&#10;drwxr-xr-x 9 root root 4096 Jul 12 15:38 .&#10;drwxr-xr-x 9 root root 4096 Jul 11 19:56 ..&#10;-rwxr-xr-x 1 root root 395 Jul 11 20:05 boot-after-backup.sh&#10;drwxr-xr-x 3 root root 4096 Jul 11 20:05 borgmatic&#10;drwxr-xr-x 2 root root 4096 Jul 11 20:05 caddy2&#10;drwxr-xr-x 3 root root 4096 Jul 11 20:05 fail2ban&#10;drwxr-xr-x 8 root root 4096 Jul 12 15:38 .git&#10;drwxr-xr-x 3 root root 4096 Jul 11 20:05 .gitea&#10;drwxr-xr-x 4 root root 4096 Jul 11 20:05 gitea&#10;-rw-r--r-- 1 root root 312 Jul 11 20:05 .gitignore&#10;-rw-r--r-- 1 root root 557 Jul 11 20:05 README.md&#10;-rwxr-xr-x 1 root root 371 Jul 11 20:05 shutdown-for-backup.sh&#10;-rw-r--r-- 1 root root 0 Jul 12 15:38 updated.txt&#10;drwxr-xr-x 2 root root 4096 Jul 11 20:05 watchtower&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="verteilen-der-konfiguration-auf-dem-server"&gt;Verteilen der Konfiguration auf dem Server&lt;/h2&gt;&#10;&lt;p&gt;Gut, damit sind die ersten Schritte getan. Ich habe nun ein vernünftig konfiguriertes Git-Repository, was meine Server-Konfiguration abbildet und vom Laptop aus gepflegt und zumindest rudimentär getestet werden kann. Außerdem kann ich über eine automatische &lt;code&gt;Action&lt;/code&gt; Updates der Konfiguration auf dem Server ablegen und eine Update-Datei mit Zeitstempel beschreiben.&lt;/p&gt;&#10;&lt;p&gt;Nun muss das Update auf dem Server noch entgegengenommen, verteilt und die betroffenen Programme und Dienste sollen neu gestartet werden. Doch dies schauen wir uns im Artikel &lt;a href="https://blog.schallbert.de/server-config-deploy/"&gt;Serverkonfiguration ausrollen&lt;/a&gt; an.&lt;/p&gt;&#10;</description></item><item><title>Stahl bearbeiten</title><link>https://blog.schallbert.de/milling-steel/</link><pubDate>Mon, 10 Jun 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/milling-steel/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-06-10-siemens-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: PC towers with CNC-made window"&#10; title="Stahl bearbeiten" /&gt;&#10;&lt;p&gt;Für unseren Retro-Gaming Stand auf der &lt;a href="https://blog.schallbert.de/makerfaire-ruhr/"&gt;Maker Faire Ruhr 2024&lt;/a&gt; habe ich Ausschnitte in PC-Gehäuse gefräst. Die zugehörige Geschichte möchte ich hier nun erzählen und passende Parameter für die Bearbeitung von Stahl auf &amp;ldquo;weichen&amp;rdquo; Hobbymaschinen teilen.&lt;/p&gt;&#10;&lt;h3 id="doch-vorsicht"&gt;Doch Vorsicht!&lt;/h3&gt;&#10;&lt;p&gt;Vorab möchte ich erwähnen, dass ich einige sicherheitsrelevante Dummheiten begangen habe, die in Zukunft unbedingt zu vermeiden sind:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Niemals mit Handschuhen in der Nähe sich drehender Maschinenteile hantieren!&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&#10;&lt;li&gt;Kühl- und Schmiermittel müssen für den Verwendungszweck geeignet sein; Rauch- und Brandgefahr!&lt;/li&gt;&#10;&lt;li&gt;Schutz- und Abschrankeinrichtungen sind fest zu montieren und dürfen nicht umgangen werden!&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Wer sich also mit Arbeits- und Betriebssicherheit auskennt, sollte das weiter unten zur Verfügung gestellte Video besser nicht anschauen.&lt;/p&gt;&#10;&lt;h2 id="das-werkstück"&gt;Das Werkstück&lt;/h2&gt;&#10;&lt;p&gt;Zu bearbeiten habe ich Stahlbleche von PC-Gehäusen (MIDI-Tower) mit einer Stärke von &lt;code&gt;0.7mm-0.9mm&lt;/code&gt;. Beim Material handelt es sich um galvanisch verzinkten, kalt gewalzten Stahl (&lt;a href="https://de.cosasteel.com/secc-steel/#SECC_Material_Specification" target="_blank" rel="noopener noreferrer" class="external-link"&gt;SECC&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;), also eine günstige und verhältnismäßig leicht zu verarbeitende Stahlsorte mit niedrigem Kohlenstoffgehalt und geringer Beimischung anderer Legierungselemente.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-06-10-cad-pcwindow-opensea.jpg" alt="Image: PC tower case service flap drawing" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Diese Stahlsorte wird für Bleche im Automobilbau und vielen Haushaltsgeräten, vor allem in &amp;ldquo;weißer Ware&amp;rdquo; eingesetzt und findet zudem oft im Automaten- und Schaltschrankbau Verwendung. Somit ist diese Fräsarbeit übertragbar auf andere Einsatzgebiete.&lt;/p&gt;&#10;&lt;p&gt;Die Maße der Gehäuseseiten liegen zwischen &lt;code&gt;35x45xm&lt;/code&gt; und &lt;code&gt;52x50cm&lt;/code&gt;, passen also alle problemlos auf meine Maschine. Mit einer Ausnahme liegen die Seitenteile mit ihrer sichtbaren Seite plan und ohne Ausschnitte auf, sodass mein Vakuumtisch für das Niederhalten ohne Weiteres in Frage kommt.&lt;/p&gt;&#10;&lt;p&gt;Die Seitenwand eines der Gehäuse besitzt kiemenartige Perforationen zur Belüftung sowie eine Vertiefung für einen Griff. Bei den Lüftungsschlitzen behelfe ich mir mit Frischhaltefolie zum Halten des Vakuumdrucks, während der Verlust des Vakuums beim Durchfräsen der Griffmulde in Kauf genommen wird.&lt;/p&gt;&#10;&lt;h3 id="cadcam"&gt;CAD/CAM&lt;/h3&gt;&#10;&lt;p&gt;Die zu schneidenden Ausschnitte sind sämtlich geometrisch einfach und berücksichtigen für Rundungen &lt;code&gt;r=1.5*Fräserradius&lt;/code&gt;. Das Fenster für den &amp;ldquo;Meeres-PC&amp;rdquo; ist dabei zweiteilig angelegt und soll eine Sturmszene auf See darstellen.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-06-10-cad-pcwindow-opensea-detail.jpg" alt="Image: CAD detail drawing for open-sea-PC" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Das Fenster ist rundherum &lt;code&gt;0.25mm&lt;/code&gt; kleiner als die Aussparung, was in einer fast spielfreien Passung führt und sehr professionell aussieht.&lt;/p&gt;&#10;&lt;p&gt;Im CAM stelle ich Gegenlauffräsen ein, was bei weniger steifen Maschinen wie meiner bei Bearbeitung harter und zäher Materialien anzuraten ist. Die Maschine läuft so etwas weicher und leiser - trotzdem schüttelt und vibriert sie noch beträchtlich.&lt;/p&gt;&#10;&lt;p&gt;Bei der Tiefenzustellung wähle ich einen Wert, der vier hundertstel Millimeter geringer ausfällt als die Materialstärke beträgt. So lasse ich praktisch nur noch eine hauchdünne Folie aus Lack und Korrosionsschutz übrig, verschleiße mir aber nicht die Haftvermittlungsmatte aus Gummi.&lt;/p&gt;&#10;&lt;h3 id="fräsparameter"&gt;Fräsparameter&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-06-10-cutter.jpg" alt="Image: endmill for cutting steel" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Im Vergleich zur Bearbeitung von Holz liegen die Schnittgeschwindigkeiten bei Stahl deutlich niedriger. Während ich in Birke Multiplex mit &lt;code&gt;450m/min&lt;/code&gt; unterwegs bin, empfehlen mir verschiedene Schnittdatenrechner wie &lt;a href="https://www.machiningdoctor.com/de/calculators/speeds-and-feeds-calculator/#f1p5" target="_blank" rel="noopener noreferrer" class="external-link"&gt;dieser hier&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Werte zwischen &lt;code&gt;80m/min&lt;/code&gt; und &lt;code&gt;200m/min&lt;/code&gt;. So ergibt sich beispielsweise ein Parametersatz wie &lt;code&gt;S4250u/min, F255mm/min&lt;/code&gt; für meinen Zweischneider aus Feinstkorn-Hartmetall.&lt;/p&gt;&#10;&lt;p&gt;Dieser Fräser besitzt im &lt;code&gt;45°&lt;/code&gt;-Winkel abgeschrägte Schnittkanten von &lt;code&gt;0.2mm&lt;/code&gt; Länge und mit &lt;code&gt;30°&lt;/code&gt; einen starken Drall. Zur Erhöhung der Standzeit ist er im Vakuumverfahren &lt;a href="https://de.wikipedia.org/wiki/Physikalische_Gasphasenabscheidung" target="_blank" rel="noopener noreferrer" class="external-link"&gt;PVD&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; mit einer etwa &lt;code&gt;3µm&lt;/code&gt; starken &lt;a href="https://en.wikipedia.org/wiki/Titanium_aluminium_nitride" target="_blank" rel="noopener noreferrer" class="external-link"&gt;TiAlN&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;-Legierung beschichtet.&lt;/p&gt;&#10;&lt;p&gt;Ich habe auf meiner Maschine nach Rücksprache mit dem Hersteller des Fräsers Parameter vorgewählt, die ich in Testläufen nochmal anpassen und damit höhere Tiefenzustellung bei geringerer Spantemperatur erreichen konnte (keine blauen Späne mehr). Hier die Werte, die ich für die Bearbeitung schließlich verwendet habe:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Fräser&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Schneidengeometrie&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Drehzahl&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Vorschub XY&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Vorschub Z&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Zustellung Z&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6mm, Vollhartmetall&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2Flutes 30°, l=12, 45°0.2Fase&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6000u/min&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;550mm/min&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;250mm/min&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.8mm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="update-fräsparameter"&gt;Update Fräsparameter&lt;/h3&gt;&#10;&lt;p&gt;Nach ein paar Rückfragen in verschiedenen Foren zum Thema habe ich neue Erkenntnisse gewonnen, die meine Fräsparameter für zukünftige Projekte nochmal deutlich verändern werden. Diese lauten:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;In der Stahlbearbeitung für die Fräser auf abgeschrägte Schnittkanten achten&lt;/li&gt;&#10;&lt;li&gt;Vorschub hoch, Tiefenzustellung runter (gilt besonders für &amp;ldquo;weiche&amp;rdquo; Maschinen)&lt;/li&gt;&#10;&lt;li&gt;Trocken fräsen ist weniger problematisch als ich dachte&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Dieses &lt;a href="https://www.youtube.com/watch?v=WzT4HJalwAs" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Youtube-Video von Stefan Gotteswinter&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; erläutert den Vorteil durch geringere Spandicken bei Verwendung abgeschrägter Schnittkanten sehr anschaulich. Dies funktioniert dann am besten, wenn die Tiefenzustellung nicht größer ist als die Länge der Fase am Schneidenende.&#10;Auch auf &lt;a href="https://www.cnczone.nl/viewtopic.php?t=23051" target="_blank" rel="noopener noreferrer" class="external-link"&gt;cnczone&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; wird mir empfohlen, einen &amp;ldquo;High-speed milling&amp;rdquo;-Ansatz zu wählen.&lt;/p&gt;&#10;&lt;p&gt;Für meinen Zweischneider passe ich die Schnittdaten also so an, dass ich die Tiefenzustellung drastisch zurücknehme, dafür aber Vorschub und Drehzahl erhöhe:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Fräser&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Schneidengeometrie&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Drehzahl&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Vorschub XY&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Vorschub Z&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Zustellung Z&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6mm Zweischneider&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;30°Drall, l=12, 45°0.2Fase&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;10600u/min&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;970mm/min&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;450mm/min&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.2mm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Ich bin schon sehr gespannt darauf, diese Werte in der Praxis zu erproben.&lt;/p&gt;&#10;&lt;h2 id="vorbereitung-des-fräsvorganges"&gt;Vorbereitung des Fräsvorganges&lt;/h2&gt;&#10;&lt;p&gt;Da ich das erste Mal in Stahl unterwegs bin, musste ich noch ein paar Besorgungen machen und Dinge vorab ausprobieren.&lt;/p&gt;&#10;&lt;h3 id="kühlschmierstoff"&gt;Kühlschmierstoff&lt;/h3&gt;&#10;&lt;p&gt;Bei Arbeiten in Stahl sei Kühlung zum Schutz des Fräsers unerlässlich, meinte der Fräsenhersteller. Also habe ich mir eine Spritze besorgt, mit der ich Schmiermittel punktgenau aufbringen kann. Nur: Welches Mittel wählen?&lt;/p&gt;&#10;&lt;p&gt;Für nur drei Teile besorge ich mir nicht extra eine Minimalmengenschmierung oder Kühlschmierstoffe aus dem Industriebedarf. Also probiere ich im Haushalt verfügbare Mittel aus:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Sprühschmierstoff WD40: Für mich nicht geeignet. Verdampft schnell, riecht.&lt;/li&gt;&#10;&lt;li&gt;Nähmaschinenöl / Feinöl: Nicht geeignet. Sehr flüssig (fließt in Vakuumlöcher), raucht und stinkt.&lt;/li&gt;&#10;&lt;li&gt;Rapsöl zum Braten: Wunderbar! Schmiert gut, relativ viskos, geruchsarm, kaum Rauchentwicklung.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="spanschutz"&gt;Spanschutz&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-06-10-chipprotector.jpg" alt="Image: This tube made of acrylic glass protects from flying chips" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Die bei der Bearbeitung entstehenden Metallspäne sind kurz, heiß und scharfkantig. Sie bleiben gern in der Kleidung hängen und fliegen so weit, dass selbst bei über einem Meter Entfernung zur Maschine noch reichlich Späne ankommen.&lt;/p&gt;&#10;&lt;p&gt;Also habe ich mir aus einem Acrylglasrohr mit &lt;code&gt;20cm&lt;/code&gt; Durchmesser ein Segment abgesägt, geschlitzt und es als Spanschutz an der Z-Achse eingehängt.&lt;/p&gt;&#10;&lt;p&gt;Mein Fehler an dieser Stelle: Ich habe auf die Klemmkraft alleine vertraut und den Spanschutz nicht zusätzlich mit Schrauben befestigt. Zum Glück verklemmte er sich erst beim Fertigen des letzten Teiles.&lt;/p&gt;&#10;&lt;h2 id="fräsen"&gt;Fräsen&lt;/h2&gt;&#10;&lt;p&gt;Man merkt sofort, dass die Maschine nicht die nötige Steifigkeit zur Verarbeitung von Stahl hat. Sobald der Fräser in das Material eintaucht, beginnt die Maschine zu rattern und zu schütteln. Dennoch kämpft sie sich tapfer durch das Material, während ich stets mit einem Tropfen Öl am Fräspfad bereit stehe.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/jbtp1jvVi2GLvJq1GdhmgF" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Stahl fräsen!&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die Spindel hat mehr als genügend Reserven zur Verfügung selbst bei der geringen Drehzahl. Jedoch achte ich für die Werkstückpositionierung sehr darauf, möglichst mittig an der Kugelumlaufspindel der Y-Achse zu arbeiten. Hier erfährt die Maschine die geringsten Verwindungskräfte. Die Späne sehen für meine Augen gut aus.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-06-10-chips.jpg" alt="Image: steel chip quality" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Das Geräusch verändert sich beim Fräsen: Ich habe mehr Vibrationen in der Maschine, wenn ich in Y-Richtung fräse, als wenn sich nur die X-Achse bewegt. Diesem Phänomen werde ich später genauer auf den Grund gehen.&lt;/p&gt;&#10;&lt;h2 id="das-ergebnis"&gt;Das Ergebnis&lt;/h2&gt;&#10;&lt;p&gt;Die Fensterausschnitte kann ich nach der Bearbeitung auf der CNC mit einem Cuttermesser einfach ausschneiden.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2024-06-10-cuttingsteel.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Video: Auslösen des Ausschnittes mit einem Cuttermesser&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die (minimalen) Grate entferne ich fix mit einer Feile.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-06-10-pcwindow-opensea.jpg" alt="Image: Cutout completed and cleaned" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Danach fertige ich Fenster aus durchsichtigem Acrylglas in &lt;code&gt;3mm&lt;/code&gt; Stärke, welche ich rundherum mit einer &lt;code&gt;10mm&lt;/code&gt; breiten Falz von &lt;code&gt;1mm&lt;/code&gt; Tiefe versehe. Somit wird die Stärke des doppelseitigen Klebebands zum Einpassen berücksichtigt und die Scheibe schließt plan mit dem PC-Gehäuse ab.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-06-10-siemens.jpg" alt="Image: The ready-built PC" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Das obige Bild zeigt ein &lt;code&gt;Celsius 1000 Workstation&lt;/code&gt; Gehäuse von Siemens, welches mit einem schlichten Fenster versehen, nun richtig edel aussieht.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Handschuhe können bei versehentlichem Kontakt mit dem Fräser mitgerissen werden. Dies kann schwere Handverletzungen nach sich ziehen.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>CNC Tuning #3</title><link>https://blog.schallbert.de/cnc-tuning-pathinterpreter/</link><pubDate>Sat, 04 May 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cnc-tuning-pathinterpreter/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-20-G64RD-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Thumbnail of endmill cutting forces"&#10; title="CNC Tuning #3" /&gt;&#10;&lt;p&gt;Dies ist der letzte Teil meiner Reihe über die Optimierung der Fertigungszeit auf CNC-Fräsmaschinen. Er konzentriert sich auf den Pfadinterpreter der CNC-Steuersoftware.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cnc-tuning-gcode/"&gt;&lt;em&gt;Teil 1&lt;/em&gt;: Besserer G-Code&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cnc-tuning-kinematics/"&gt;&lt;em&gt;Teil 2&lt;/em&gt;: Kinematische Parameter hochdrehen&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Der Pfadinterpreter der Steuersoftware ist dafür zuständig, die Anweisungen aus dem &lt;code&gt;G-Code&lt;/code&gt; zu lesen und in Maschinenbewegungen zu übersetzen. Soll beispielsweise &lt;code&gt;G01 X100 Y200 Z-2.0 F2000 S18000&lt;/code&gt; ausgeführt werden, so muss der Pfadinterpreter die akuelle Position der Maschine kennen und einen Fräsvektor erzeugen, der zur gewünschten Position mit den vorgegebenen Parametern zeigt.&lt;/p&gt;&#10;&lt;p&gt;Hierbei muss der Interpreter die kinematischen Parameter der Maschine berücksichtigen: Mit welcher Rampe auf Zielgeschwindigkeit &lt;code&gt;F1000&lt;/code&gt; beschleunigt wird ob der gewünschte Vorschub überhaupt zulässig ist, und ab welchem Punkt wieder gebremst werden muss um den Zielpunkt exakt zu erreichen. Die so gewonnenen Daten werden schließlich in eine Schrittfolge für die drei Schrittmotoren übersetzt, in einen Pufferspeicher geladen und schließlich in Echtzeit an die Motorendstufen ausgegeben.&lt;/p&gt;&#10;&lt;h2 id="schritt-3-optimierung-des-pfadinterpreters"&gt;Schritt 3: Optimierung des Pfadinterpreters&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-05-04-laf-settings.jpg" alt="Image: Look Ahead Feed in the CNC control software" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Viele Steuersoftware-Anbieter haben integrierte Fräspfadfilter programmiert, welche mittels Kommando &lt;code&gt;G64&lt;/code&gt; oder per Menü zu konfigurieren sind. Ich verlinke hier mal ein paar Beispiele verschiedener Hersteller, wo für spezifische Details nachgelesen werden kann:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://www.datron.com/resources/blog/toolpath-filter-achieves-a-perfect-surface-finish/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&amp;ldquo;Toolpath filter&amp;rdquo; von Datron&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://docs.edingcnc.com/settings/trajectory" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&amp;ldquo;Look Ahead Feed&amp;rdquo; von EdingCNC&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="http://wiki.linuxcnc.org/cgi-bin/wiki.pl?TrajectoryControl" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&amp;ldquo;Path Blending&amp;rdquo; von LinuxCNC&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Die Kunst ist nun, eine Konfiguration passend zum Fräsjob und der Maschinenkinematik zu finden. Es geht hier immer um einen Kompromiss zwischen Fertigungsgeschwindigkeit und Bahntoleranz. Soll also mit hohem Vorschub gefahren werden, werden eigentlich spitze Kanten verrundet, verbundene Liniensegmente in einem Toleranzband angefahren oder Pfade so vereinfacht, dass außerhalb der von der Maschine erreichbaren Beschleunigung befindliche Pfadpunkte nicht angefahren werden.&lt;/p&gt;&#10;&lt;h3 id="grundlagen-was-ist-look-ahead-feed"&gt;Grundlagen: Was ist &amp;ldquo;Look Ahead Feed&amp;rdquo;?&lt;/h3&gt;&#10;&lt;p&gt;Hiermit ist gemeint, dass der Pfadinterpreter der Steuersoftware sich während des Ausführens einer Fräsoperation - z.B. &lt;code&gt;G01 X* Y* (lineare Bewegung Punkt zu Punkt)&lt;/code&gt; - bereits die nächste(n) Operation(en) anschaut und herausfindet, ob und wie stark zwischen den Operationen abgebremst und wieder beschleunigt werden muss bzw. direkt durchgefahren werden kann. Die Physik dahinter erkläre ich &lt;a href="https://blog.schallbert.de/portal-milling-setup/#acceleration"&gt;im Artikel zum CNC-Setup&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Hierdurch kann teils massiv Zeit gespart werden, der Fräsvorgang wird flüssiger und die Maschine erfährt geringere Belastungen. Zusätzlich zur Vermeidung von Beschleunigungs- und Bremsvorgängen kann mittels Parameter &lt;code&gt;G64&lt;/code&gt; eingestellt werden, ob und wie Fräspfadfilter die geplante Trajektorie verändern dürfen, um weiter auf höhere Bahngeschwindigkeiten zu optimieren.&lt;/p&gt;&#10;&lt;h3 id="ideal-im-gravurbetrieb"&gt;Ideal im Gravurbetrieb&lt;/h3&gt;&#10;&lt;p&gt;Im Gravurbetrieb kann man sich solche Optionen hervorragend zu Nutze machen: Höhere Toleranzen sind hier meist OK, und leicht verrundete Kanten oft akzeptabel. Bei 3D-Gravuren dauert das Fräsen durch die vielen Pfade lang, und hier ist besonders viel Zeit herauszuholen.&lt;/p&gt;&#10;&lt;p&gt;Da ich Eding CNC benutze, kann ich nur für Parameter dieser Steuerung Tipps geben. Nun aber ab ins Detail zur Erklärung der Einstellungen!&lt;/p&gt;&#10;&lt;h3 id="fräspfadtoleranz-g64-p"&gt;Fräspfadtoleranz &lt;code&gt;G64 P*&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-05-04-lafblending.jpg" alt="Image: Look Ahead Feed G64 CNC corner blending" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Dieser Parameter gibt der Steuersoftware die allgemeine Toleranz der Pfadgenauigkeit vor.&lt;/p&gt;&#10;&lt;p&gt;Ich habe meine Maschine so eingestellt, dass sie im Gravurbetrieb Abweichungen vom vorgegebenen Fräspfad von bis zu &lt;code&gt;LAF blending tolerance = 0.3mm&lt;/code&gt; zulässt. Alternativ kann die Konfiguration per Kommando &lt;code&gt;G64 P0.3&lt;/code&gt; erfolgen. Im Bild habe ich skizziert, wie sich eine solche Einstellung in der Praxis auswirken kann: Der Fräspfad (weiß) besteht aus &lt;code&gt;G01&lt;/code&gt;-Liniensegmenten. Die Toleranz ist mit Hilfe von Kreisen eingezeichnet. Der Pfadinterpreter der Steuersoftware kann nun eigenständig Kreissegmente so definieren, dass innerhalb der Toleranz eine möglichst hohe Bahngeschwindigkeit erreicht wird.&lt;/p&gt;&#10;&lt;p&gt;Die so ermittelte Bahn (türkis) ähnelt einer Spline-Kurve. Zu beachten ist hier, dass sämtliche Linien von dieser Einstellung betroffen sein können - auch solche, die strenge Toleranzanforderungen haben. Ist dies nicht gewünscht, so sollte hier ein kleinerer Wert verwendet werden.&lt;/p&gt;&#10;&lt;h3 id="pfadverrundung-g64-r"&gt;Pfadverrundung &lt;code&gt;G64 R*&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;Treffen zwei Liniensegmente im Winkel von bis zu &lt;code&gt;LAF full speed blending angle threshold = 12°&lt;/code&gt; aufeinander, wird die Maschine ohne abzubremsen versuchen, dem Winkel genau zu folgen. Bei größeren Winkeln wird mit reduzierter Geschwindigkeit mit per &lt;code&gt;G64 P*&lt;/code&gt; definierter Toleranz gefahren. Diese Konfiguration wird mit &lt;code&gt;G64 R12&lt;/code&gt; programmiert.&lt;/p&gt;&#10;&lt;p&gt;Die Werte hier sind mit großer Vorsicht einzustellen, weil sie zu Beschleunigungsspitzen auf der Maschine führen. Große Werte können Schrittverluste und höheren Verschleiß der CNC-Fräse verursachen. Dafür werden Bahngeschwindigkeit und Genauigkeit optimal gehalten.&lt;/p&gt;&#10;&lt;h3 id="pfadverrundung-bei-reduziertem-vorschub-g64-s-d"&gt;Pfadverrundung bei reduziertem Vorschub &lt;code&gt;G64 S* D*&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-05-04-G64RD.jpg" alt="Image: Look Ahead Feed G64 CNC corner radius" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Diese Konfiguration gibt der Steuersoftware die Möglichkeit, Kanten bis zum eingestellten Toleranzwert zu verrunden: &lt;code&gt;LAF reduced speed path smoothing delta = 0.001mm&lt;/code&gt;. In Kombination mit der Verrundung muss der Winkel eingestellt werden, bis zu dem diese Verrundung durchgeführt werden soll: &lt;code&gt;LAF reduced speed path smoothing angle threshold = 0°&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Programmiert wird diese Funktion am Besten in Kombination mit der Pfadverrundung bei vollem Vorschub als &lt;code&gt;G64 R* S* D*&lt;/code&gt;. Der Vorteil hierbei ist, dass Liniensegmenten wie im Bild gezeigt sehr genau gefolgt wird, und die Kanten gleichzeitig aber fein justierbar abgerundet werden. Bis zum über &lt;code&gt;R&lt;/code&gt; definierten Winkel wird sogar mit vollem Vorschub dem Pfad exakt gefolgt.&lt;/p&gt;&#10;&lt;p&gt;Werden bei &lt;code&gt;S&lt;/code&gt; sehr große Winkel eingestellt, so findet entsprechend &lt;code&gt;D&lt;/code&gt; auch eine stärkere Verrundung statt. Überschreitet der im Fräsprogramm vorgegebene Winkel jedoch den konfigurierten Wert, so fällt der Algorithmus auf die für &lt;code&gt;P&lt;/code&gt; hinterlegten Werte bzw. in den exakten Pfadfolgemodus zurück.&lt;/p&gt;&#10;&lt;h3 id="pfadvereinfachung-g64-q"&gt;Pfadvereinfachung &lt;code&gt;G64 Q*&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-05-04-lafcombine.jpg" alt="Image: Look Ahead Feed G64, path combine setting" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Enthält die G-Code Datei zahlreiche, sehr kurze Liniensegmente (wie sie oft bei der Erstellung von Pfaden aus Bilddateien entstehen), kann diese Funktion durch Vereinfachen der Fräspfade Abhilfe schaffen. Dabei gibt die Zahl hinter dem Parameter die Toleranz an, bis zu der Liniensegmente zu einer Linie verschmolzen werden.&lt;/p&gt;&#10;&lt;p&gt;Diese Einstellung entlastet den Pufferspeicher der Maschine und erhöht gleichzeitig die mittlere Bahngeschwindigkeit. Für den Gravurbetrieb halte ich diese Einstellung insbesondere für interessant, wenn mit &amp;ldquo;verrauschten&amp;rdquo; Daten gearbeitet wird und im CAD nicht die Möglichkeit bestand, die Anzahl der Pfade zu reduzieren.&lt;/p&gt;&#10;&lt;h3 id="beschleunigungsfilter-g64-f"&gt;Beschleunigungsfilter &lt;code&gt;G64 F*&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;Mittels diesen Parameters können die durch Look Ahead Feed möglicherweise erzeugten Beschleunigungsspitzen abgemildert werden. Der minimal einzustellende Wert ergibt sich durch den Quotienten aus gewähltem Vorschub für die Operation und maximaler Geschwindigkeit der Maschine.&lt;/p&gt;&#10;&lt;p&gt;Beispiel: &lt;code&gt;Fmin = Foperate / Fg00&lt;/code&gt;, bei &lt;code&gt;Fg00 = 12000mm/min&lt;/code&gt; und &lt;code&gt;Foperate = 5000mmm/min&lt;/code&gt; ist &lt;code&gt;Fmin = 0.42&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Also wird bei Vorschub von &lt;code&gt;5000mm/min&lt;/code&gt; die maximal mögliche Beschleunigung der Maschine abgerufen und Kurvenradien so eingestellt, dass sie bei konstanter Bahngeschwindigkeit gefahren werden. Alle Werte größer gleich &lt;code&gt;Fmin = 1&lt;/code&gt; sind inherent sicher, unabhängig vom gewählten Vorschub.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-05-04-accelfilter.jpg" alt="Image: acceleration filter for G64 operation" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Dieser Parameter wurde entwickelt für die Bearbeitung von elastischen Materialien wie Gummi oder Kork, wo eine konstante Vorschubgeschwindigkeit extrem wichtig und die Bahntreue eher zweitrangig ist. Der Parameter wird in Kombination mit &lt;code&gt;G64 R*&lt;/code&gt; verwendet. Die folgende Programmierung für das obige Beispiel würde die volle Kapazität der Maschine abrufen: &lt;code&gt;G64 R120 F0.42&lt;/code&gt;, wobei mit &lt;code&gt;F5000&lt;/code&gt; gefahren wird, &lt;code&gt;F12000&lt;/code&gt; der Maximalvorschub der Maschine ist und spitze Winkel bis &lt;code&gt;120°&lt;/code&gt; ohne abzubremsen gefahren werden sollen.&lt;/p&gt;&#10;&lt;p&gt;Wende ich diese Werte auf meine Maschine an, entsteht das nebenstehende Bild. Hier soll ein Rechteck mit &lt;code&gt;25 x 100mm&lt;/code&gt; Kantenlänge gefertigt werden. Weiß ist wieder die Zieltrajektorie dargestellt. Die türkise Kurve zeigt die durch die Programmierung des Beschleunigungsfilters gefahrene Kurve mit konstanter Bahngeschwindigkeit. Je geringer der Vorschub im Verhältnis zum Maximalvorschub und je höher die Beschleunigung der Maschine eingestellt ist, desto kleiner wird der notwendige Radius.&lt;/p&gt;&#10;&lt;p&gt;Für den Gravurbetrieb und das Fräsen fester Materialien wird dieser Parameter meiner Erfahrung nach jedoch nicht verwendet.&lt;/p&gt;&#10;&lt;h2 id="meine-konfiguration-für-die-hobbyline-4530"&gt;Meine Konfiguration für die Hobbyline 4530&lt;/h2&gt;&#10;&lt;p&gt;Ich habe im Gravurbetrieb viel mit den verschiedenen LAF-Parametern herumgespielt und eine Menge Rohlinge als Lehrgeld &amp;ldquo;verbraucht&amp;rdquo;. Die kürzesten Fertigunszeiten erhielt ich bei vorheriger Optimierung der kinematischen Parameter mit folgender Konfiguration:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&lt;code&gt;G64 Q0.3 R12 D0.3 S120&lt;/code&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Damit sage ich der Maschine folgendes:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Verwende den Pfadvorschau-Algorithmus und versuche, kurze Liniensegmente unter &lt;code&gt;0.3mm&lt;/code&gt; Länge zu verschmelzen. Versuche, den Vorschub bei einem Winkel zweier Streckenabschnitte von bis zu &lt;code&gt;12°&lt;/code&gt; nicht herunterzuregeln. Bei größeren Winkeln bis &lt;code&gt;120°&lt;/code&gt; kann statt einem exakten Nachfahren mit bis zu &lt;code&gt;r=0.3mm&lt;/code&gt; verrundet werden. Bei noch größeren Winkeln ist dem vorgegebenen Pfad zu folgen und, falls erforderlich, bis zum Stillstand abzubremsen.&amp;rdquo;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h2 id="fazit"&gt;Fazit&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-05-04-duplicated-software.jpg" alt="Image: dual install of CNC control software for different purposes" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Da mich die Parameteroptimierung für den Gravurbetrieb ziemlich weit vom Parametersatz für den normalen Fräsbetrieb weggebracht haben, habe ich mein Steuerprogramm einfach ein zweites Mal installiert.&lt;/p&gt;&#10;&lt;p&gt;Dort konnte ich dann Geschwindigkeit, Achsbeschleunigung und Pfadparametrierung getrennt festlegen. Nun habe ich das Beste beider Welten auf einer Maschine zur Verfügung:&lt;/p&gt;&#10;&lt;p&gt;Einen sehr genauen, weich operierenden Fräsbetrieb mit folgenden Parametern:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;Fmax XY 12000mm/min&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Fmax Z 4800mm/min&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Amax X 1000mm/s²&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Amax Y 1000mm/s²&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Amax Z 1000mm/s²&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;G64 P0.1 R6&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Und über die andere Verknüpfung einen überaus flinken Gravurbetrieb:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;Fmax XY 12000mm/min&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Fmax Z 4800mm/min&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Amax X 4000mm/s²&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Amax Y 3200mm/s²&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Amax Z 1600mm/s²&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;G64 Q0.3 R12 D0.3 S120&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="und-wo-ist-der-beweis"&gt;Und wo ist der Beweis?&lt;/h2&gt;&#10;&lt;p&gt;Das eingebettete Video zeigt dasselbe Gravurprojekt mit vier verschiedenen Optimierungsstufen:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;oben links: Herstellerkonfiguration der Maschine, Standard-Fräspfade aus CAD/CAM&lt;/li&gt;&#10;&lt;li&gt;oben rechts: Geschwindigkeit und Beschleunigung auf Fräsbetrieb erhöht&lt;/li&gt;&#10;&lt;li&gt;unten links: Look Ahead feed eingeschaltet &lt;code&gt;G64 P0.3&lt;/code&gt;, Fräspfade im CAM optimiert&lt;/li&gt;&#10;&lt;li&gt;unten rechts: Optimiert für Gravurbetrieb wie oben beschrieben, CAM Flyover auf &lt;code&gt;2mm reduziert&lt;/code&gt; und Leerfahrten minimiert&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/2EWe1ZvVW9TKY6k3XtpmWF" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;CNC-Tuning für den Gravurbetrieb&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>CNC Tuning #2</title><link>https://blog.schallbert.de/cnc-tuning-kinematics/</link><pubDate>Sat, 27 Apr 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cnc-tuning-kinematics/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-27-kinematics-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Thumbnail of a CNC machine&amp;#39;&amp;#39;s portal X-Axis"&#10; title="CNC Tuning #2" /&gt;&#10;&lt;p&gt;Dies ist der zweite Teil meiner Reihe über die Optimierung der Fertigungszeit auf CNC-Fräsmaschinen. Er konzentriert sich auf die kinematischen Maschinenparameter für den Gravurbetrieb.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cnc-tuning-gcode/"&gt;&lt;em&gt;Teil 1&lt;/em&gt;: Besserer G-Code&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cnc-tuning-pathinterpreter/"&gt;&lt;em&gt;Teil 3:&lt;/em&gt; Pfadinterpreter optimieren&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="schritt-2-tuning-der-kinematischen-parameter-der-cnc"&gt;Schritt 2: Tuning der kinematischen Parameter der CNC&lt;/h2&gt;&#10;&lt;p&gt;Die theoretischen Grundlagen habe ich im &lt;a href="https://blog.schallbert.de/portal-milling-setup/"&gt;Artikel zur CNC-Steuerungskonfiguration&lt;/a&gt; ausführlich erläutert. Meine Probleme damit und zugehörige Lösungen habe ich nach vielen Tests und Rücksprache mit dem Hersteller im &lt;a href="https://blog.schallbert.de/projects/one-year-zerspanobert/#velocityerror"&gt;Fazit zu einem Jahr CNC-Betrieb&lt;/a&gt; festgehalten. Die folgenden Abschnitte wenden die obigen Erkenntnisse auf eine Demonstrationsmaschine an, die ich mir habe leihen können.&lt;/p&gt;&#10;&lt;h3 id="verfahrgeschwindigkeit-im-eilgang"&gt;Verfahrgeschwindigkeit im Eilgang&lt;/h3&gt;&#10;&lt;p&gt;Für die Demomaschine konnte ich die Geschwindigkeit im Eilgang von den vorgeschlagenen &lt;code&gt;Fxy = 4m/min&lt;/code&gt; und &lt;code&gt;Fz = 2.4m/min&lt;/code&gt; auf &lt;code&gt;Fxy = 12m/min&lt;/code&gt; und &lt;code&gt;Fz = 4.8m/min&lt;/code&gt; erhöhen. Setze ich alle drei Achsen gleichzeitig in Bewegung, erhalte ich durch Steigung der Kugelgewindespindeln und Mikroschrittkonfiguration (8 pro Vollschritt) der Motoren eine kombinierte Schrittfrequenz von &lt;code&gt;96kHz&lt;/code&gt;, was unter der maximalen Schrittfrequenz von &lt;code&gt;125kHz&lt;/code&gt; der Steuerung liegt.&lt;/p&gt;&#10;&lt;h3 id="beschleunigung-im-eilgang"&gt;Beschleunigung im Eilgang&lt;/h3&gt;&#10;&lt;p&gt;Die Herstellerangaben empfehlen hier &lt;code&gt;aXY = 300mm/s²&lt;/code&gt; und &lt;code&gt;aZ = 200mm/s²&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Ich habe die Beschleunigung in mehreren Schritten hochgesetzt und bin in einer ersten Runde bei &lt;code&gt;aXY = 1000mm/s²&lt;/code&gt; und &lt;code&gt;aZ = 400mm/s²&lt;/code&gt; gelandet. Diese Werte sind für diese kleine und leichte Maschine mit nur &lt;code&gt;30x45x14cm&lt;/code&gt; Verfahrweg nach meiner Erfahrung für den Normalbetrieb ohne Weiteres möglich. Sie erhöhen die Fertigungsgeschwindigkeit stark, denn jeder Richtungswechsel, auch die im Materialeingriff bei &lt;code&gt;G01, G02, G03&lt;/code&gt;, werden nun um Faktor &lt;code&gt;3&lt;/code&gt; bzw. &lt;code&gt;2.5&lt;/code&gt; schneller ausgeführt.&lt;/p&gt;&#10;&lt;p&gt;So habe ich die Maschine oft betrieben und auch auf der Messe bin ich mit diesen Werten stabil und sicher gefahren. Das folgende Video zeigt die Fräse im Gravurbetrieb mit den angegebenen Werten.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/vvmwT8e6vWnj5zbuqMdUfN" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;CNC-Tuning: Vorschau&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Danach jedoch wollte ich wissen, wie weit ich es treiben kann. Getreu dem vorher beschriebenen Motto&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Wer die Grenze nicht überschreitet, weiß nicht, wo sie liegt&amp;rdquo;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;habe ich erst einmal geprüft, wie sich massive Schrittverluste bei dieser Maschine anhören. Also habe ich eine Achse mit einem Holzbalken blockiert und bin vorsichtig dagegen gefahren:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2024-04-27-soundoflosingsteps.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Massive step loss soundcheck&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Das deutlich vernehmbare Rattern entsteht wenn der Motorhochlauf zwar klappt, in Richtung Zielgeschwindigkeit aber diverse Vollschritte verloren gehen. Wird der Rotor bereits bei Anlauf des Motors &amp;ldquo;verloren&amp;rdquo;, so fiept der Motor nur noch, ohne die Achse überhaupt in Bewegung zu setzen. Verliert der Motor nur wenige Schritte, befindet sich aber auf Zielgeschwindigkeit, so gibt es einen rauen und unter Umständen kratzenden Ton.&lt;/p&gt;&#10;&lt;p&gt;Ich habe für das Experiment die X-Achse gewählt, weil sie sich am einfachsten blockieren lässt. Des Weiteren habe ich sie in &lt;code&gt;X+&lt;/code&gt;-Richtung blockiert. Begründung: diese Maschine besitzt kein professionelles Festlager zur Aufnahme von Druck- und Zugkräften der Kugelumlaufspindel. Stattdessen werden Druckkräfte in Richtung Schrittmotor sicher von einem auf der Spindel montierten Flansch abgefangen, der gegen in der Portalwange befindliche Kugellager drückt.&lt;/p&gt;&#10;&lt;p&gt;Zugkräfte hingegen werden hauptsächlich von den Motorlagern aufgenommen - da wollte ich kein Risiko eingehen.&#10;Nun ist also das Geräusch bei plötzlichem und ruckartigen Schrittverlust bekannt - das Übertakten kann beginnen!&lt;/p&gt;&#10;&lt;p&gt;Meine Vorgehensweise:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Die Beschleunigung einer Achse der Maschine verdoppeln&lt;/li&gt;&#10;&lt;li&gt;Die betreffende Achse umher joggen, dabei auf merkwürdige Geräusche achten und die Achsbewegung beobachten&lt;/li&gt;&#10;&lt;li&gt;Vorige Schritte wiederholen, bis massive Schrittverluste auftreten&lt;/li&gt;&#10;&lt;li&gt;Beschleunigung auf den Mittelwert zwischen &amp;ldquo;klappt&amp;rdquo; und &amp;ldquo;klappt nicht&amp;rdquo; zurückfahren&lt;/li&gt;&#10;&lt;li&gt;Den vorigen Schritt solange wiederholen, bis die Maschine sauber fährt&lt;/li&gt;&#10;&lt;li&gt;Alle Schritte auch für die anderen Achsen durchführen&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;So bin ich schließlich bei für eine solche Hobbymaschine unglaublichen &lt;code&gt;aXY = 4000mm/s²&lt;/code&gt;, &lt;code&gt;aZ = 1600mm/s²&lt;/code&gt; gelandet. Dies liegt nun eine Größenordnung über den empfohlenen Werten.&lt;/p&gt;&#10;&lt;p&gt;Die kleine Fräse scheint auch hier stabil zu laufen und die gefertigten Gravuren sehen gut aus. Allerdings konnte ich mir nicht vorstellen, dass ich Schrittmotoren, Treiberstufen und Netzteil noch innerhalb ihrer Spezifikation betreibe. Daher habe ich gemessen.&lt;/p&gt;&#10;&lt;h3 id="elektrische-messwerte"&gt;Elektrische Messwerte&lt;/h3&gt;&#10;&lt;p&gt;Das Netzteil ist mit &lt;code&gt;3.7A&lt;/code&gt; bei &lt;code&gt;36V&lt;/code&gt; angegeben. Ich darf diesen Wert also in der Summe weder im Eilgang &lt;code&gt;G00&lt;/code&gt; noch im Materialeingriff dauerhaft überschreiten. Wie oben erklärt, ist der Eilgang im Gravurbetrieb durch die geringeren Fräskräfte maßgeblich, sodass ich besonderes Augenmerk auf das Verfahren mehrerer Achsen gleichzeitig legen muss.&lt;/p&gt;&#10;&lt;p&gt;Die Motoren können je einen Dauerstrom von &lt;code&gt;3.0A&lt;/code&gt; verkraften, limitieren mir hier also nichts. In den elektrischen Tests schaue ich lediglich, ob ich mit der Energieaufnahme des Systems im Rahmen bleibe.&lt;/p&gt;&#10;&lt;h4 id="messaufbau"&gt;Messaufbau&lt;/h4&gt;&#10;&lt;p&gt;Ich schließe das aus &lt;a href="https://blog.schallbert.de/measure-inrush-current/"&gt;diesem Artikel bekannte&lt;/a&gt; Multimeter direkt am Ausgang des Netzteils an und nehme Spannung und Strom auf. Für das Experiment simuliere ich die Fertigung der oben im Video verlinkten Gravur und führe in zwei Durchläufen zuerst eine zeitlich grob aufgelöste Leistungsfaktoranalyse (&lt;code&gt;2Hz&lt;/code&gt;) durch, gefolgt von einer fein aufgelösten Stromermittlung mit &lt;code&gt;20kHz&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h4 id="ergebnisse"&gt;Ergebnisse&lt;/h4&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-27-powerdiagram-engrave.jpg" alt="Image: Power diagram for tuned engraving" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Im obigen Bild sind Spannung, Strom, Wirk- und Blindleistung aufgetragen. Zu erkennen ist, dass die Blindleistung (&lt;code&gt;grün&lt;/code&gt;) immer dann kurze Spitzen zeigt, wenn mehrere Achsen gleichzeitig in den Eilgang beschleunigen. Die Leistungsaufnahme einer Achse im Eingriff &lt;code&gt;G01/G02/G03, blau&lt;/code&gt; beträgt im Schnitt etwa &lt;code&gt;45W&lt;/code&gt; und ist ähnlich hoch wie bei der Konstantfahrt im Eilgang.&lt;/p&gt;&#10;&lt;p&gt;Das Netzteil ist in der Lage, die Spannung (&lt;code&gt;orange&lt;/code&gt;) ziemlich konstant zu halten und knickt selbst unter Volllast nur um knapp &lt;code&gt;0.2V&lt;/code&gt; ein. Einzig der Stromwert sah mir etwas zu glatt aus. Ich vermutete an dieser Stelle, dass die zeitliche Auflösung zu gering ist und beschloss, das Experiment mit Konzentration auf den Strom zu wiederholen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-27-currentdiagram-engrave.jpg" alt="Image: Current diagram for tuned engraving" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Und tatsächlich. Die höhere zeitliche Auflösung zeigt, dass die Stromaufnahme bei Beschleunigung mehrerer Achsen Spitzen zeigt, die über den bereits bekannten Werten liegt.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-27-currentpeak-engrave.jpg" alt="Image: Zoomed-in current draw for axis acceleration" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Hier stechen Bewegungen auf der Y-Achse, welche das gesamte Portal in Bewegung setzen muss, besonders heraus. Und zwar genau dann, wenn die anderen Achsen noch in Bewegung sind oder gerade gehalten haben. Dies hängt mit den Schrittmotortreibern zusammen. Sie betreiben die Motoren nämlich noch etwa &lt;code&gt;0.5s&lt;/code&gt; nach Anhalten bei vollem Strom weiter. Die dadurch entstehende Überlagerung lässt die Spitzen noch dramatischer aussehen.&lt;/p&gt;&#10;&lt;h4 id="benchmark-spitzenlast"&gt;Benchmark Spitzenlast&lt;/h4&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-27-loadtest.jpg" alt="Image: Zoomed-in current draw for axis acceleration" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Na dann treiben wir es mal auf die Spitze mit einem Beschleunigungs-Benchmark. Wird das Netzteil auch bei voller Last unter Beschleunigung aller Motoren gleichzeitig die Ausgangsspannung halten können?&lt;/p&gt;&#10;&lt;p&gt;Um dies zu testen, habe ich einen G-Code geschrieben, der alle Achsen gleichzeitig auf Höchstgeschwindigkeit beschleunigen und dann wieder abbremsen lässt. Anschließend wird unter gleichen Bedingungen zum Ausgangspunkt zurückgefahren und das Ganze wiederholt.&lt;/p&gt;&#10;&lt;p&gt;Auf diese Weise kann ich herausfinden, ob das Netzteil genügend Reserven hat, um Lastspitzen abzufangen und gleichzeitig schauen, ob die Motoren bereits im größeren Maße Schritte verlieren. Im Mittel liegt hier die aufgebrachte Leistung bei &lt;code&gt;95W&lt;/code&gt; und die Versorgungsspannung sinkt um &lt;code&gt;0.28V&lt;/code&gt; auf &lt;code&gt;35.72V&lt;/code&gt; ab. Dabei muss das Netzteil in der Spitze &lt;code&gt;5.14A&lt;/code&gt; abgeben, was knapp &lt;code&gt;40%&lt;/code&gt; über Nennstrom liegt.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2024-04-20-benchmark.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Benchmark&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Hört man ganz genau hin, so klingt die Maschine direkt zu Beginn des Benchmarks etwas rau, fängt sich dann aber. So können auch Schrittverluste klingen. Es ist daher sehr wichtig, die Maschine mit allen Sinnen zu überwachen.&lt;/p&gt;&#10;&lt;h3 id="mechanische-prüfung"&gt;Mechanische Prüfung&lt;/h3&gt;&#10;&lt;p&gt;Nun zur letzten Prüfung: Ich gebe beim Eilgang-Benchmark zusätzlich Last auf die Achsen, indem ich während des Programmablaufes mit meinem Arm an der jeweiligen Achse ziehe. Die Achsen Z und X laufen stabil weiter. Bei der Portalachse &lt;code&gt;Y&lt;/code&gt; merke ich jedoch, dass die Maschine nun anders klingt. Irgendwie rau, als würde man mit mit einer Raspel leicht über eine Holzplatte ziehen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-27-steplossy.jpg" alt="Image: Y-axis reveals massive step loss" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Lege ich eine große Last an, so fährt der Schrittmotor auf dieser Achse nicht mal mehr an, sondern fiept nur noch. Er kann den Pulsen der Endstufe nicht mehr folgen. Ich reduziere nun also die Beschleunigung schrittweise, bis der Motor die Portalachse auch unter mittlerer Last wiederholbar und ohne hörbare Schrittverluste beschleunigen kann. Hier lande ich am Ende bei immer noch flinken &lt;code&gt;[aX = 4000mm/s² aY = 3200mm/s² aZ = 1600mm/s²]&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;In einem letzten Schritt führe ich nach fünfmaliger Wiederholung des Benchmarks eine weitere Referenzfahrt durch und schaue mir die Logs der Steuersoftware an. Alle Achsen sind nun unauffällig hinsichtlich Schrittverlusten.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-27-steplossxz.jpg" alt="Image: XZ-axis without step loss" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="appelle-zum-cnc-tuning"&gt;Appelle zum CNC-Tuning&lt;/h3&gt;&#10;&lt;p&gt;Bitte geht beim Tuning eurer Maschine behutsam vor. Lernt sie richtig kennen, bevor ihr damit beginnt. Hört genau hin und optimiert Achse für Achse nacheinander. Nehmt euch Zeit für Tests, Prüfungen und Experimente.&lt;/p&gt;&#10;&lt;p&gt;Selbst wenn ihr eine sehr ähnliche Maschine wie meine besitzt, machen andere Abmessungen bereits einen riesigen Unterschied hinsichtlich Steifigkeit und damit auch möglicher Verfahrgeschwindigkeit bzw. Beschleunigung. Sogar Wartungszustand, leichtgängigere Wellen, andere Führungen o.Ä. können dazu führen, dass ihr am Ende mit anderen Werten fahrt als ich.&lt;/p&gt;&#10;&lt;p&gt;Es wird kein Profi-Equipment benötigt, um die Grenzen der Maschine auszuloten. Nicht einmal eine Messuhr, da Schrittverluste auch mit Hilfe der Referenzschalter hinreichend genau bestimmt werden können.&lt;/p&gt;&#10;&lt;p&gt;Alles was ich durch die elektrischen Messungen oben festgestellt habe, ist, dass der Hersteller bei der Auslegung der Maschine sauber gearbeitet hat und keine Komponente unterdimensioniert ist. Im Gegenteil, diese Maschine ist für die Übertaktung bestens geeignet.&lt;/p&gt;&#10;&lt;p&gt;Und nun weiter zum letzten &lt;a href="https://blog.schallbert.de/cnc-tuning-pathinterpreter/"&gt;&lt;em&gt;Teil 3:&lt;/em&gt; Pfadinterpreter optimieren&lt;/a&gt;.&lt;/p&gt;&#10;</description></item><item><title>CNC Tuning #1</title><link>https://blog.schallbert.de/cnc-tuning-gcode/</link><pubDate>Sat, 20 Apr 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cnc-tuning-gcode/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-20-camtuned-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Thumbnail of CAM simulation for G-code optimizing"&#10; title="CNC Tuning #1" /&gt;&#10;&lt;p&gt;Dies ist der erste Teil meiner Reihe über die Optimierung der Fertigungszeit auf CNC-Fräsmaschinen. Er konzentriert sich auf den Einsatz von CAD- und CAM-Werkzeugen zur Erzeugung effizienteren G-Codes.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cnc-tuning-kinematics/"&gt;&lt;em&gt;Teil 2&lt;/em&gt;: Kinematische Parameter hochdrehen&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cnc-tuning-pathinterpreter/"&gt;&lt;em&gt;Teil 3:&lt;/em&gt; Pfadinterpreter optimieren&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Es gibt diverse Maßnahmen zur Reduktion der Fertigungszeit mit einer CNC-Maschine. Erstmal jedoch sollte beleuchtet werden, warum so etwas gewünscht ist und welche möglichen Nachteile sich hierdurch ergeben.&lt;/p&gt;&#10;&lt;h2 id="warum-überhaupt"&gt;Warum überhaupt?&lt;/h2&gt;&#10;&lt;p&gt;Aus kaufmännischer Sicht sorgen geringere Fertigungszeiten für einen höheren Teiledurchsatz und ermöglichen damit einen höheren Umsatz in einer gegebenen Zeitspanne. Zudem ist die Investition der Maschine schneller amortisiert.&lt;/p&gt;&#10;&lt;p&gt;Als Hobbyist sind kürzere Fertigungszeiten vor allem dann interessant, wenn das Ziel die Herstellung sehr komplexer Geometrien oder die 3D-Fertigung ist. Hier können Fräszeiten durchaus mehrere Stunden betragen und die möglichen Einsparungen sind folglich besonders groß.&lt;/p&gt;&#10;&lt;p&gt;Bei mir steht wie auch in der Lehre der wissenschaftliche Gedanke im Vordergrund: Ich will herauszufinden, wo die Grenze der Maschine liegt. Ich möchte wissen, warum sie dort liegt. Ich werde versuchen, sie stabil innerhalb ihrer Grenzen zu betreiben. Dabei ist der Verschleiß unter Kontrolle zu halten.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Um diese Grenze jedoch genau zu kennen, muss sie zuerst übertreten werden. Woher sonst soll ich wissen, wo sie sich befindet?&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h3 id="was-sind-mögliche-nachteile"&gt;Was sind mögliche Nachteile?&lt;/h3&gt;&#10;&lt;p&gt;Bestimmte Komponenten einer &amp;ldquo;übertakteten&amp;rdquo; Maschine könnten schneller als üblich verschleißen. Dazu zählen etwa Netzteile und Motor-Endstufen, die höhere transiente Ströme verkraften müssen. Lager und Führungen werden durch höhere Beschleunigungen stärker beansprucht. Umlaufspindeln und Maschinenrahmen müssen die dadurch resultierenden, stärkeren und schneller wechselnden Kräfte und Vibrationen aufnehmen und können früher Ermüdungserscheinungen oder sich lösende Verbindungen aufweisen.&lt;/p&gt;&#10;&lt;p&gt;Bei der Ermittlung der Grenzen kann die Maschine im Extremfall sogar beschädigt werden: Mit hoher Wahrscheinlichkeit treten jenseits der Grenze Schrittverluste&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; auf. Das ist zwar im Testbetrieb nicht weiter problematisch. Doch stimmt die angenommene Position dann nicht mehr mit der tatsächlichen Position überein und es besteht das Risiko des &amp;ldquo;Crashes&amp;rdquo; - wenn nicht mit Bedacht vorgegangen wird. Hier noch ein ziemlich gutes &lt;a href="https://www.youtube.com/watch?v=C_9dM1wx0Sg" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Youtube-Video von &lt;em&gt;gammaflow&lt;/em&gt; zum Thema Schrittverluste vermeiden&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Werden die Fräsbahnen im CAD oder CAM zugunsten kürzerer Fertigungszeiten verändert, so kann dies außerdem negative Auswirkungen auf Schnitt-, Kanten- und Oberflächenqualität haben.&lt;/p&gt;&#10;&lt;p&gt;Bei der Optimierung des Pfadinterpreters der Steuersoftware ist ebenfalls ein Kompromiss einzugehen: Höhere Fertigungsgeschwindigkeit wird hier mit geringerer Genauigkeit und/oder höherem Maschinenverschleiß eingekauft.&lt;/p&gt;&#10;&lt;h3 id="welche-maßnahmen-werden-nicht-diskutiert"&gt;Welche Maßnahmen werden nicht diskutiert?&lt;/h3&gt;&#10;&lt;p&gt;Nicht betrachten werde ich hier die Optimierung von Vorschub &lt;code&gt;F&lt;/code&gt; und Drehzahl &lt;code&gt;S&lt;/code&gt; des gewählten Fräsers im Eingriff. Diese Parameter hängen vom zu bearbeitendem Material, der Fräsergeometrie, den Fähigkeiten der Maschine und dem Mut des Bedienpersonals ab, sodass ich keine allgemein gültigen Tunings vorschlagen kann.&lt;/p&gt;&#10;&lt;p&gt;Einen anderen, großen Einflussfaktor muss ich hier ebenfalls ausklammern: Die Bearbeitungsstrategie des CAM-Programms bezogen auf Eilgänge. Mein CAM-Programm lässt mich da nicht besonders tief eingreifen, sodass ich für die Optimierung der letzten paar Prozent Fertigungszeit stets von Hand im G-Code arbeiten muss, was entsprechend fehleranfällig ist.&lt;/p&gt;&#10;&lt;h2 id="tuning-in-drei-schritten"&gt;Tuning in drei Schritten&lt;/h2&gt;&#10;&lt;p&gt;Bei meiner Optimierung bin ich in drei Schritten vorgegangen, die allerdings unabhängig voneinander umgesetzt werden können. Jeder Schritt hat mir hierbei deutlich spürbare Verbesserungen gebracht. Dieser Artikel konzentriert sich aus mehreren Gründen auf den Gravurbetrieb:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Gravuren erlauben meist größere Toleranzen als andere Betriebsarten. Dadurch kann stärker verrundet werden, was zu geringeren Beschleunigungen führt. Dies ermöglicht im Mittel größere Vorschübe&lt;/li&gt;&#10;&lt;li&gt;Die zu erwartenden Fräskräfte sind geringer: Keine Durchfräsungen, kaum Energieeinsatz bei der Spanverdrängung, kleineres Spanvolumen als bei Schaftfräsern&lt;/li&gt;&#10;&lt;li&gt;Dadurch kann die Maschine näher an ihren mechanischen und elektrischen Grenzen betrieben werden&lt;/li&gt;&#10;&lt;li&gt;Durch im Schnitt größere G-Code Dateien mehr Optimierungspotential&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="schritt-1-besserer-g-code"&gt;Schritt 1: Besserer G-Code&lt;/h2&gt;&#10;&lt;p&gt;Dieser Abschnitt folgt dem Credo &lt;code&gt;Garbage in, Garbage out&lt;/code&gt;. Will heißen: Wenn mein G-Code schon schlecht ist, kann die Maschine niemals optimale Fertigungszeiten erreichen. Was zeichnet also &amp;ldquo;schlechten&amp;rdquo; G-Code in Bezug auf Fertigungszeit aus?&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;übermäßig komplex und lang, möglicherweise Dateigröße im Megabyte-Bereich&lt;/li&gt;&#10;&lt;li&gt;extrem viele Stützpunkte und eine Menge kurzer Liniensegmente&lt;/li&gt;&#10;&lt;li&gt;Einen Haufen &amp;ldquo;Leerfahrten&amp;rdquo; im Eilgang auf XY mit hochgezogener Z-Achse&lt;/li&gt;&#10;&lt;li&gt;Viele Rückzüge der Z-Achse auf Sicherheitshöhe&lt;/li&gt;&#10;&lt;li&gt;Häufige Wartezeiten der Maschine (Spindelhochlauf, Kühlmittelpumpe, Controller-Puffer voll)&lt;/li&gt;&#10;&lt;li&gt;Übermäßiges Feinstschlichten auch bei B-Oberflächen&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="aber-wie-soll-ich-meinen-g-code-vereinfachen"&gt;Aber wie soll ich meinen G-Code vereinfachen?&lt;/h3&gt;&#10;&lt;p&gt;Kurze Frage, lange Antwort: Es fängt im CAD an und hört möglicherweise im CAM nicht auf.&lt;/p&gt;&#10;&lt;h3 id="beispiel-cad"&gt;Beispiel CAD&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-20-bline.jpg" alt="Image: Letter &amp;#39;B&amp;#39; in CAM, drawn as a set of line segments" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Nehmen wir an, wir möchten eine Schrift fräsen. Unser CAD-Programm hat auch einen Haufen toller Schriftarten dazu. Wir erzeugen nun eine DXF-Datei für&amp;rsquo;s CAM. Im CAM dann sehen wir plötzlich beim Hineinzoomen, dass alle Buchstaben aus sehr kurzen Liniensegmenten bestehen statt aus Kreissegmenten oder &lt;a href="https://en.wikipedia.org/wiki/Spline_%28mathematics%29" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Splines&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Die Folge: Das CAM wird &lt;code&gt;G01&lt;/code&gt;-Bewegungen, also &amp;ldquo;Linear Moves&amp;rdquo; bzw. eine Aneinanderreihung von Strecken erzeugen.&lt;/p&gt;&#10;&lt;p&gt;Die verbundenen Strecken machen sehr viele Richtungswechsel der Maschine erforderlich. Folglich muss sie vor dem Verbindungspunkt ständig abbremsen und danach wieder beschleunigen. Die Maschine kann also nicht mit konstanter Geschwindigkeit fahren, denn das würde eine unendliche Beschleunigung am Knickpunkt erfordern.&lt;/p&gt;&#10;&lt;p&gt;Für dieses Problem gibt es Lösungen, die im Idealfall gemeinsam angewendet werden:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Im CAD-Programm einstellen, dass wann immer möglich Splines oder Kurven statt Liniensegmenten erstellt werden sollen&lt;/li&gt;&#10;&lt;li&gt;Vereinfachung der Zeichnung durch Reduktion der Stützstellen&lt;/li&gt;&#10;&lt;li&gt;Vermehrte Nutzung von Radien und Splines an Kanten, Vermeidung von Winkeln&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;In meinem Falle war die erste Option nicht verfügbar, sodass ich nun ein anderes Programm für die Gravur von Schriften verwende als für meine üblichen Zeichnungen. Dadurch kann ich Schrift jetzt in Linien- und Bogensegmenten ausdrücken. Die dafür erforderliche Fräszeit halbiert sich und gleichzeitig kommt der G-Code mit weniger Zeilen aus. Außerdem wird die Schrift runder und hält sich sogar noch genauer an die anfangs gewählte Schriftart.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-20-bsplinevsline.jpg" alt="Image: Letter &amp;#39;B&amp;#39; in CAM, twice. Once per spline, once as line segments" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Gute Beispiele für die Vereinfachung von Pfaden durch Reduktion von Stützpunkten liefern Gimp und Inkscape - für Inkscape habe ich das sogar bereits meinem &lt;a href="https://blog.schallbert.de/path-from-image/#inkscape"&gt;Artikel zum Thema Bild-zu-Pfad-Konversion&lt;/a&gt; gezeigt. Mit ein paar wenigen Klicks erhält man auf Kosten der Genauigkeit Zeichnungsdaten, die teils deutlich schneller zu fertigen sind.&lt;/p&gt;&#10;&lt;p&gt;Da ein Schaftfräser sowieso keine Tasche mit &lt;code&gt;r=0&lt;/code&gt; in den Ecken fertigen kann, habe ich mir angewöhnt, sämtliche Ecken und Kanten im CAD abzurunden. Nicht nur liegen so hergestellte Teile besser in der Hand, sie sind außerdem schneller zu fertigen - die Zeitersparnis summiert sich, besonders auf langsamen und trägen Maschinen.&lt;/p&gt;&#10;&lt;h3 id="beispiel-cam"&gt;Beispiel CAM&lt;/h3&gt;&#10;&lt;p&gt;Im CAM kann ich durch vermehrte Verwendung von Anfahrpunkten und Rampen ein wenig Zeit sparen. Ich lege sie zumeist an das kürzeste Liniensegment und spare so einen Brems/Beschleunigungsvorgang. Bei der Eintauchart ist der Vorteil von &amp;ldquo;Rampe&amp;rdquo; gegenüber &amp;ldquo;Helix&amp;rdquo;, dass sämtliche Wege des Fräsers Nutzwege sind und ich ohne zusätzliche Achsbeschleunigungen durchfahren kann. Je nach Rampensteigung allerdings verlängert sich der Fräspfad, da Überlappungen entstehen.&lt;/p&gt;&#10;&lt;p&gt;Einen viel größeren Effekt auf die Fertigungszeit hat die kluge Auswahl von Räumzustellungen und -strategien für Taschen passend zu Material, Fräser und gewünschter Oberflächengüte. So erzeugt Wirbelfräsen (Trochoidalfräsen&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;) in vielen Fällen längere Dateien und kostet auf der Maschine mehr Zeit als der klassische &amp;ldquo;Rasenmäher&amp;rdquo;, welcher die Tasche bloß abzeilt. Für die Räumzustellung wähle ich bei Materialien wie Holz und Acrylglas gern große Werte über &lt;code&gt;60%&lt;/code&gt; - es sei denn, der Boden der Tasche muss besonders hohen Qualitätsansprüchen genügen.&lt;/p&gt;&#10;&lt;p&gt;Für Gravuren ist es meiner Meinung nach oft am Schnellsten, auf einen Räumgang mit einem zweiten Fräser zu verzichten und wenn möglich einen Gravurfräser mit abgeflachter Spitze - ähnlich derer für meine &lt;a href="https://blog.schallbert.de/cut-dibond-tests/"&gt;Tests mit Dibond&lt;/a&gt; - zu verwenden. Dieser benötigt zum Räumen zwar länger; das kann ich durch gesteigerte Gravurtiefe und eine recht breite Abflachung von &lt;code&gt;1.5mm&lt;/code&gt; aber kompensieren. Ich verwende bei einem solchen Fräser eine Räumzustellung in XY von &lt;code&gt;1mm&lt;/code&gt;. Übrig bleibt schließlich der Zeitvorteil durch weniger Werkzeugwechsel.&lt;/p&gt;&#10;&lt;h3 id="aber-wie-soll-ich-die-leerfahrten-reduzieren"&gt;Aber wie soll ich die Leerfahrten reduzieren?&lt;/h3&gt;&#10;&lt;p&gt;Endlich mal ein paar knackig-kurze Antworten:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Geringere Rückzugshöhe in &lt;code&gt;Z&lt;/code&gt; (ich verwende &lt;code&gt;5mm&lt;/code&gt; aber &lt;code&gt;2mm&lt;/code&gt; würde bei mir wahrscheinlich auch funktionieren)&lt;/li&gt;&#10;&lt;li&gt;Optimierung der Frässtrategie im CAM auf &amp;ldquo;minimale Wege&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;Bei Nesting: Darauf achten, dass die Geometrie möglichst nicht Zeile für Zeile, sondern Teil für Teil gefertigt wird.&lt;/li&gt;&#10;&lt;li&gt;Im Zweifel Optimierung der Strategie im G-Code von Hand. Lohnt sich dann, wenn das CAM-Programm nicht gut genug arbeiten kann und viele Gleichteile zu fertigen sind&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="und-wo-ist-das-problem-mit-rückzügen-der-z-achse"&gt;Und wo ist das Problem mit Rückzügen der Z-Achse?&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Rückzüge finden vor allem im Gravurbetrieb sehr häufig statt&lt;/li&gt;&#10;&lt;li&gt;der Zeitverlust summiert sich&lt;/li&gt;&#10;&lt;li&gt;Beim Wiedereingriff wird meist nicht im Eilgang sondern mit für den Fräser vorgesehener Geschwindigkeit mit &lt;code&gt;G01&lt;/code&gt; auf das Material gefahren&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Eine Verbesserung kann hier eine gute Planung der Fräsreihenfolge im CAM bringen. Möglichst viele Sektionen sollten verbunden und ohne Absetzen des Fräsers gefertigt werden. Im CAM sollte für Rückzug und Wiedereinfahrt in &lt;code&gt;Z&lt;/code&gt; der Eilgang verwendet werden.&lt;/p&gt;&#10;&lt;h3 id="wie-reduziere-ich-wartezeiten"&gt;Wie reduziere ich Wartezeiten?&lt;/h3&gt;&#10;&lt;p&gt;Mit Kühlschmierstoffschaltvorgängen, Wechsel der Spindeldrehzahl oder des Werkzeugs sind Wartezeiten verbunden, die meist leicht zu reduzieren sind:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Kühlschmierstoffförderung durchlaufen lassen während des Programmablaufes&lt;/li&gt;&#10;&lt;li&gt;die Verwendung möglichst weniger Werkzeuge bei gleichbleibender Drehzahl für ein Produkt&lt;/li&gt;&#10;&lt;li&gt;Anschläge verwenden, um die Maschinenbeschickung zu vereinfachen und Nullpunkte nicht immer wieder neu setzen zu müssen&lt;/li&gt;&#10;&lt;li&gt;Wird die Maschine mit komplett verschiedenen parametern für Gravur- und Fräsbetrieb gefahren, empfiehlt sich die doppelte Installation der Steuersoftware mit jeweils passenden Parametersätzen. Dadurch entfällt das ständige Ändern der Konfiguration.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Weiter geht es in &lt;a href="https://blog.schallbert.de/cnc-tuning-kinematics/"&gt;&lt;em&gt;Teil 2&lt;/em&gt;: Kinematische Parameter hochdrehen&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Schrittverluste treten dann auf, wenn die Achse eines Schrittmotors nicht mehr synchron mit der durch die Steuerung vorgegebenen Schrittfolge dreht, sondern weniger bzw. mehr Schritte zurücklegt als vorgegeben. Fast immer ist die Ursache eines Schrittverlustes in einer unzureichenden Auslegung des Systems (z.B. zu geringe oder instabile Versorgungsspannung, zu schwache oder nicht an den Motor angepasste Endstufen) oder einer Überlastung des Schrittmotors zu finden. Selten jedoch können Schrittverluste Symptome von Defekten sein wie Lagerfraß, Windungsschluss oder durchschlagene Pufferkondensatoren.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:2"&gt;&#10;&lt;p&gt;Das Trochoidalfräsen ist keine schlechte Praxis. Es hat andere Stärken, ist im Gravurrbetrieb aber nur selten von Nutzen.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Vakuumspannen</title><link>https://blog.schallbert.de/vacuum-clamping/</link><pubDate>Tue, 02 Apr 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/vacuum-clamping/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-cuttingforces-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Thumbnail of endmill cutting forces"&#10; title="Vakuumspannen" /&gt;&#10;&lt;p&gt;Wie ich im &lt;a href="https://blog.schallbert.de/makerfaire-ruhr/#verz%c3%b6gerungen"&gt;Artikel zur Maker Faire Ruhr 2024 erwähnte&lt;/a&gt;, hatte ich große Probleme beim Spannen meiner Werkstücke für die Fertigung von Spirographen.&lt;/p&gt;&#10;&lt;p&gt;In diesem Beitrag möchte ich genauer darauf eingehen, eine Analyse der Ursachen vornehmen und schließlich ein paar Lösungsmöglichkeiten vorstellen.&lt;/p&gt;&#10;&lt;p&gt;Was ich vorwegnehmen kann: Sämtliche Probleme lassen sich mit Wissen über die Ursachen sowie etwas Zeit-, Planungs- und Materialaufwand beheben.&lt;/p&gt;&#10;&lt;h2 id="das-problem"&gt;Das Problem&lt;/h2&gt;&#10;&lt;p&gt;Ich hatte wie üblich die Bahnplanung für die Spirographen so festgelegt, dass ich erst die inneren Bereiche (Taschen, Teile, Bohrungen) und danach die äußeren Bereiche fräsen lasse. Ganz zum Schluss sollten Ausschnitte und Fasen gefertigt werden. Des Weiteren sah ich vor, die Teile doppelseitig zu bearbeiten, damit auch die Rückseiten der Zahnräder gebrochene Kanten und weiche Übergänge aufweisen würden.&lt;/p&gt;&#10;&lt;h3 id="vakuumverlust"&gt;Vakuumverlust&lt;/h3&gt;&#10;&lt;p&gt;Bei meinem ersten Versuch bin ich so vorgegangen wie ich das im &lt;a href="https://blog.schallbert.de/projects/spirograph/"&gt;Spirographen-Projekt&lt;/a&gt; mit dem Material Acrylglas bereits erfolgreich durchführen konnte: Die Rückseite wird zuerst bearbeitet, aber nicht ganz durchgefräst. Nach dem Wenden des Werkstückes dann soll nur noch mit dem Fasenfräser ausgeschnitten und gleichzeitig die Kanten gebrochen werden.&lt;/p&gt;&#10;&lt;p&gt;Dies funktionierte für die erste Aufspannung auch hervorragend.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2024-04-02-spiro-back.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Rückseitige Bearbeitung der Spirographen.&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Bei der zweiten jedoch ging mit dem Durchstoßen der Oberfläche so viel Unterdruck verloren, dass sich das Material vom Vakuumtisch löste und seine ursprüngliche, leichte Wölbung annahm. Somit musste ich den Fräsvorgang abbrechen und mir eine andere Strategie zum Niederhalten überlegen.&lt;/p&gt;&#10;&lt;p&gt;Ich konnte mir zu diesem Zeitpunkt noch nicht richtig erklären, warum jetzt so viel mehr Druck verloren ging als bei der Fertigung in Acrylglas.&lt;/p&gt;&#10;&lt;h3 id="frischhaltefolie"&gt;Frischhaltefolie&lt;/h3&gt;&#10;&lt;p&gt;Ich probierte mit Frischhaltefolie dem Druckverlust entgegenzuwirken. Dabei stellten sich gleich mehrere Folgeschwierigkeiten: Fährt der Fräser durch die Folie, so wird sie beim Fräsen geschlossener Konturen vom Fräser erfasst und in Windeseile um selbigen gewickelt. Der Fräser schneidet nun nicht mehr, sondern reibt nur noch am Material, was sehr schnell zu Hitzespuren am Holz führt.&lt;/p&gt;&#10;&lt;p&gt;Doch selbst wenn ich die Frischhaltefolie vorab befestige: Da ich Teile ausschneide, ist beim ersten Eindringen in die auf der Unterseite bereits vorhandene Nut der &lt;em&gt;lokale Unterdruck&lt;/em&gt; am Teil bereits wieder so klein, dass es sich kaum an Ort und Stelle halten lässt.&lt;/p&gt;&#10;&lt;h3 id="verdrehung-des-werkstücks"&gt;Verdrehung des Werkstücks&lt;/h3&gt;&#10;&lt;p&gt;Für den zweiten Versuch passte ich die Strategie an: Der Schaftfräser würde das Werkstück durchfräsen, so dass ich keine zweite Aufspannung benötigte. Ich wollte die Kantenbrechung auf der Rückseite später optional über eine auf dem Tisch festgespannte Halterung realisieren, in die ich die Teile würde einlegen können.&lt;/p&gt;&#10;&lt;p&gt;Während das Ausschneiden der Teile noch problemlos funktionierte (erneut die &amp;ldquo;inneren&amp;rdquo; Teile zuerst fräsen), so bekam ich beim Anbringen der Fase bereits auf der Vorderseite große Probleme. Die Zahnräder begannen, sich zu verdrehen und die Schablone mit Hohlrad war überhaupt nicht niederzuhalten.&lt;/p&gt;&#10;&lt;h3 id="zu-wenig-zeit"&gt;Zu wenig Zeit&lt;/h3&gt;&#10;&lt;p&gt;Ich musste also schon bei der Bearbeitung der Vorderseite auf speziell für die Werkstücke angefertigte Halterungen zurückgreifen. Deren Konstruktion und Fertigung nahm Zeit in Anspruch die ich an dieser Stelle so nicht eingeplant hatte.&lt;/p&gt;&#10;&lt;p&gt;Vor der Messe konnte ich mich nicht in der Tiefe mit der Analyse beschäftigen - eine schnelle Lösung musste her.&lt;/p&gt;&#10;&lt;h2 id="die-ursachen"&gt;Die Ursachen&lt;/h2&gt;&#10;&lt;p&gt;Mit ein paar Wochen Abstand kann nun ich sagen, dass mehrere Faktoren zu den Problemen führten. So kommen eine schwierige Geometrie, ungünstige Materialeigenschaften und bestimmte Eigenheiten des Vakuumtischdesigns zusammen. Aber gehen wir dem Ganzen hier mal auf den Grund.&lt;/p&gt;&#10;&lt;h3 id="werkstück"&gt;Werkstück&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-rawmaterial.jpg" alt="Image: Veneered MDF raw material" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Das Ausgangsmaterial ist mitteldichte Faserplatte von &lt;code&gt;10mm&lt;/code&gt; Stärke. Sie ist einseitig mit &lt;code&gt;0.75mm&lt;/code&gt; starkem Olivenholzfurnier versehen. Dieses Material ist für die Bearbeitung auf einem Vakuumtisch bereits von Grund auf problematisch:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Durch Furnierung und Lagerung sind die Platten leicht gewölbt&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;, wollen sich also von meinem Vakuumtisch abheben.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;MDF ist luftdurchlässig, sodass die Vakuumpumpe mit größeren Leckageströmen zu tun hat als üblicherweise (so bekomme ich bei Aufspannung der Platte nur &lt;code&gt;-300mBar&lt;/code&gt; zusammen statt der üblichen &lt;code&gt;-800mBar&lt;/code&gt; ohne Leckströme). Die geringe Materialdicke verstärkt diesen Effekt.&lt;/li&gt;&#10;&lt;li&gt;MDF ist nicht nur senkrecht zur Plattenebene luftdurchlässig, sondern auch waagerecht. Dadurch sind die Haltekräfte in der Nähe von Fräsungen stark reduziert, denn Luft kann nun auch seitlich in das Material eindringen und erhöht so zusätzlich die Leckageströme.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Das hier verlinkte Video &lt;a href="https://www.youtube.com/watch?v=osJik4XjZTY" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Youtube: Verschiebekraftmessungen von &lt;em&gt;Anton CNC&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zeigt anschaulich, wie sich verschiedene Materialien auf einem Vakuumtisch mit poröser Oberfläche verhalten. Ebenfalls sehr empfehlenswert zum Thema ist dieses Video &lt;a href="https://www.youtube.com/watch?v=jk7Clrvtlmo" target="_blank" rel="noopener noreferrer" class="external-link"&gt;YouTube: Haltekraftermittlung während des Zerspanens von &lt;em&gt;vakuumtischDE&lt;/em&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; um einen Eindruck zu gewinnen, unter welcher Belastung das Werkstück während des Fräsvorganges stehen kann.&lt;/p&gt;&#10;&lt;h3 id="geometrie"&gt;Geometrie&lt;/h3&gt;&#10;&lt;p&gt;Mein Design für die Spirographen lässt sich nicht besonders gut niederhalten:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Die Schablone mit Hohlrad besitzt dünne Wände von nur etwa &lt;code&gt;2cm&lt;/code&gt; Stärke. Am dicksten sind sie an den Seiten links und rechts, weisen in der Mitte aber eine große Aussparung auf, durch die Luft in alle Richtungen in das Material eindringen kann und so die Haltekräfte reduziert.&lt;/li&gt;&#10;&lt;li&gt;Das Zahnrad verfügt über Löcher zur Aufnahme von Stiften. Um die Löcher herum ist der Anpressdruck reduziert.&lt;/li&gt;&#10;&lt;li&gt;Die Zähne von Hohlrad und Zahnrad liegen so frei, dass sie durch Querströme der Luft im Material praktisch keinen Beitrag zur Haltekraft leisten können. Gleichzeitig bieten die Zahnköpfe aber einen großen Hebel für Verdrehung oder Verschiebung des Werkstücks: Eine ungünstige Paarung.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-spirograph-on-vacuum-table.jpg" alt="Image: Spirograph on vacuum table: hole grid overlay" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Nehme ich das als Grundlage für die Praxis, so sind nur die im Bild oben innerhalb des Kreises befindlichen Vakuumlöcher überhaupt in der Lage, nennenswerte Spannkräfte zu erzeugen. Diese sind aber wiederum nahe am Zentrum und bieten folglich nur wenig Widerstand gegen Verdrehen des Werkstücks. Hinzu kommt, dass das Zahnrad hier mehrere Bohrungen und damit zusätzliche &amp;ldquo;Lecks&amp;rdquo; aufweist.&lt;/p&gt;&#10;&lt;h3 id="fräskräfte"&gt;Fräskräfte&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-cuttingforces-z.jpg" alt="Image: Cutting forces XY" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Die durch das Werkzeug (den Fräser) in das Material eingebrachten Kräfte wirken stets in XY-Richtung, bei V-cut Fräsern und Schaftfräsern mit Spirale/Drall zusätzlich in Z-Richtung.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Kräfte durch Vorschub: Je schneller durch das Material gefahren wird, desto stärkere Kräfte in XY-Richtung wirken auf das Material in Verfahrrichtung.&lt;/li&gt;&#10;&lt;li&gt;Kräfte durch Fräserdrehung bzw. Gleich- oder Gegenlauf: Verwendet man einen Schaftfräser ohne Drall, so entstehen Kräfte orthogonal in der Ebene der Verfahrrichtung. Fahre ich also in positive X-Richtung, wird der Fräser auch in Y-Richtung eine Kraft ausüben, die durch seine Eigendrehung eingebracht wird: Im Gleichlauf möchte der Fräser dem Material ausweichen, wird also nach &lt;code&gt;Y+&lt;/code&gt; abgelenkt (wie &lt;a href="https://blog.schallbert.de/cnc-router-overload/"&gt;in diesem Artikel anschaulich gezeigt&lt;/a&gt;). Im Gegenlauf ist die Ablenkrichtung invers und der Fräser möchte nach &lt;code&gt;Y-&lt;/code&gt; ausweichen.&lt;/li&gt;&#10;&lt;li&gt;Kräfte durch Fräsergeometrie: Weist der Fräser eine Spirale auf oder ist er in der Geometrie abgeschrägt, so wirken zusätzlich Kräfte in Z-Richtung. Bei Vcut- und Downcut-Fräsern wird das Material auf das Maschinenbett gedrückt, bei Upcut-Spiralfräsern hingegen wird der Span wie gewünscht ausgeworfen. Das Werkstück erfährt aber auch eine Kraft, die es im Extremfall vom Maschinenbett abheben kann.&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-cuttingforces-xy.jpg" alt="Image: Cutting forces Z" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/li&gt;&#10;&lt;li&gt;Wird in der Vollnut gefräst (d.h. der Fräser ist statt an der Werkstückkante entlang zu fahren über seinen kompletten Durchmesser im Eingriff), erhöhen sich die Kräfte quer zur Fräsrichtung sogar noch: Im Bild fährt er am oberen Rand der Nut im Gegenlauf, was den Fräser &amp;ldquo;vom Material weg&amp;rdquo;, im Bild also nach unten, drückt. Gleichzeitig fährt er am unteren Nutrand im Gleichlauf, was ihn &amp;ldquo;in das Material&amp;rdquo; zieht - ebenfalls nach unten.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="vakuumspannsystem"&gt;Vakuumspannsystem&lt;/h3&gt;&#10;&lt;p&gt;Wie in den Artikeln &lt;a href="https://blog.schallbert.de/cnc-vacuum-pumps/"&gt;über Vakuumpumpen&lt;/a&gt; und &lt;a href="https://blog.schallbert.de/why-vacuum-table/"&gt;Vakuumtische&lt;/a&gt; beschrieben, verfüge ich über eine trockenlaufende Drehschieberpumpe, angeschlossen an einen Lochrastervakuumtisch. Bei der Bearbeitung poröser Werkstoffe ist diese Kombination ebenso wie Rastervakuumtische mit kleinvolumigen Pumpen problematisch:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Mein Lochrastervakuumtisch besitzt Löcher mit einem Vakuumkanal von &lt;code&gt;0.5mm&lt;/code&gt; Durchmesser. Durch diese Engstelle wird die Pumpe zwar bei offenliegenden Löchern entlastet, denn sie muss nicht den vollen Luftstrom bringen, die das &lt;code&gt;5mm&lt;/code&gt; große Sackloch ermöglicht. Allerdings kann das Loch durch den begrenzten Luftstrom in Kombination mit MDF weniger Unterdruck aufbauen.&lt;/li&gt;&#10;&lt;li&gt;Durch die hohen Leckströme ist meine Vakuumpumpe mit ihrem Volumenstrom an ihrer Grenze. Hier sind andere Pumpentypen deutlich besser geeignet (siehe &lt;a href="https://blog.schallbert.de/cnc-vacuum-pumps/#radial-blower"&gt;Radialgebläse&lt;/a&gt;, &lt;a href="https://blog.schallbert.de/cnc-vacuum-pumps/#seitenkanalverdichter"&gt;Seitenkanalverdichter&lt;/a&gt;).&lt;/li&gt;&#10;&lt;li&gt;Ein Vakuumtisch spannt generell nur nach unten (in Z-Richtung). Allerdings müssen auch Kräfte in XY-Richtung abgefangen werden, damit es nicht zu einer Verschiebung oder Drehung des Werkstückes kommt. Diese sind über den &lt;a href="https://blog.schallbert.de/cnc-vacuum-pumps/#parallelverschiebung"&gt;Haftreibkoeffizienten µ&lt;/a&gt; zwar an den Unterdruck am Werkstück gekoppelt (und lässt sich über Materialpaarung und Oberflächenbeschaffenheit beeinflussen), wenn Letzterer aber zu niedrig ausfällt, wird das Werkstück nicht zuverlässig gehalten.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-vacuumtable-channels-porousmaterials.jpg" alt="Image: Air flow system of vacuum table and porous material" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Obwohl sich die Vakuumkanäle im &lt;code&gt;10mm&lt;/code&gt;-Raster befinden, beträgt der für den Leckstrom maßgebliche Abstand nur &lt;code&gt;5mm&lt;/code&gt;. Die Ränder der Sacklöcher (je &lt;code&gt;5mm&lt;/code&gt;Durchmesser) befinden sich nämlich in diesem Abstand zueinander, sodass der Luftstrom quer zum Material eine geringere Distanz zurücklegen muss als durch das Material hindurch.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h4 id="vergleich-zum-widerstandsnetzwerk"&gt;Vergleich zum Widerstandsnetzwerk&lt;/h4&gt;&#10;&lt;p&gt;Die tatsächliche Spannkraft lässt sich gut abschätzen wenn man sich Pumpe, Vakuumtisch und Werkstück als elektrisches Netzwerk vorstellt. Ich habe hierfür ein Modell vorbereitet und stelle es im Folgenden vor.&lt;/p&gt;&#10;&lt;h4 id="widerstände-vakuumtisch-werkstück"&gt;Widerstände: Vakuumtisch, Werkstück&lt;/h4&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-tableworkpiece.jpg" alt="Image: Vacuum table and workpiece" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Die Sauglöcher des Vakuumtisches stellen Widerstände für den Luftstrom dar, welche sich in einer Parallelschaltung befinden. In Reihe dazu ist das Werkstück erneut als Widerstand geschaltet, was mit seinem anderen Pol an &amp;ldquo;Masse&amp;rdquo;, also dem Umgebungsluftdruck, liegt.&lt;/p&gt;&#10;&lt;h4 id="vakuumverteiler--schalter-manometer--spannungsmesser"&gt;Vakuumverteiler = Schalter, Manometer = Spannungsmesser&lt;/h4&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-manoswitch.jpg" alt="Image: Vacuum manifold and Manometer" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Das Manometer im Saugkanal kann als Spannungsmessgerät gesehen werden. Den Vakuumverteiler mit seinen Ventilen zum Schalten der Vakuumsektionen sehe ich als elektrische Schalteinrichtung. Ist der Schalter geschlossen, so stellt er die Verbindung zwischen Pumpe und Vakuumtisch her. Ist er geöffnet, so blockiert er den Luftstrom.&lt;/p&gt;&#10;&lt;h4 id="vakuumpumpe--spannungsquelle"&gt;Vakuumpumpe = Spannungsquelle&lt;/h4&gt;&#10;&lt;p&gt;In diesem Bild stellt die Pumpe eine Spannungsquelle dar, die in meinem Falle maximal &lt;code&gt;U = 8V&lt;/code&gt; entsprechend &lt;code&gt;-800mBar&lt;/code&gt; bereitstellen kann. Durch den bei realen Spannungsquellen vorhandenen inneren Widerstand von &lt;code&gt;0.2Ohm&lt;/code&gt; kann sie einen Kurzschlussstrom von &lt;code&gt;I = 40A&lt;/code&gt; erzeugen, analog zu den &lt;code&gt;40m³/h&lt;/code&gt; Durchsatz. Dieser Vergleich ist physikalisch natürlich nicht korrekt, denn ich müsste hier mit Masse- statt Volumenströmen arbeiten. Außerdem hängt die Fördermenge nicht überall im Kennfeld linear mit dem Druck zusammen und die von mir angenommenen Widerstandswerte sind nur Richtgrößen. Zur Modellierung des Sachverhaltes eignet es sich meiner Meinung nach trotzdem.&lt;/p&gt;&#10;&lt;p&gt;Bei Werkstückmaterialien wie Acrylglas oder Metall ist der Luftdurchfluss nahe Null, ihr &amp;ldquo;Widerstand&amp;rdquo; ist also extrem hoch. Dadurch fällt hier der allergrößte Teil der Spannung (Unterdruck) ab, sodass das Werkstück fest gespannt wird.&lt;/p&gt;&#10;&lt;p&gt;Bei Verwendung luftdichter Materialien ist die Welt entsprechend in Ordnung: Das Manometer zeigt beim noch unbearbeiteten Werkstück den den Druck auf dem Material korrekt an an. Selbst bei Durchfräsungen wird lediglich der Widerstand des Materials &amp;ldquo;in Reihe&amp;rdquo; zu den betroffenen Sauglöchern zu Null gesetzt. So wird der Unterdruck und damit Haltekraft am Teil selbst kaum reduziert.&lt;/p&gt;&#10;&lt;p&gt;Schaut man genau hin, so ist zu erkennen, dass das Manometer die Druckdifferenz vor dem Vakuumverteiler misst. Sein Wert ist also nur dann auch aussagekräftig für die Spannkraft, wenn das Werkstück einen sehr hohen Durchströmungswiderstand bietet.&lt;/p&gt;&#10;&lt;h4 id="sonderfall-poröse-werkstoffe"&gt;Sonderfall poröse Werkstoffe&lt;/h4&gt;&#10;&lt;p&gt;Ganz anders sieht das Bild bei porösen Werkstoffen wie MDF aus:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-porousmaterial-circuitdiagram.jpg" alt="Image: equivalent circuit diagram for porous material on a vacuum table" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Hier liegt der Durchgangswiderstand des Materials nur knapp eine Größenordnung über dem der Vakuumlöcher. Ich habe die Werte durch Versuche am Vakuumtisch ermittelt. Sie basieren auf Druckmessungen und Berechnungen von Werkstückfläche und der Anzahl vom Material abgedeckter, aktiver Sauglöcher. Besonders hervorzuheben sind hier zwei Effekte, die mir das Niederhalten zusätzlich schwer gemacht haben:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Augenmerk auf die Widerstände im Material in Parallelschaltung zum Durchgangswiderstand in &lt;code&gt;Z-Richtung&lt;/code&gt;. Sie entstehen dadurch, dass die Luft sich nun auch &amp;ldquo;zwischen den Löchern&amp;rdquo; durch das Material bewegen kann.&lt;/li&gt;&#10;&lt;li&gt;Auf der rechten Seite des Bildes ist zu erkennen, was passiert, wenn das Werkstück in einen inaktiven Vakuumbereich hineinragt: Die Sauglöcher sind untereinander praktisch ohne Widerstand mittels Vakuumkanälen im Tisch verbunden. Da das Werkstück &amp;ldquo;Querluftzug&amp;rdquo; in &lt;code&gt;XY-Richtung&lt;/code&gt; ermöglicht, muss durch Verbindung der Sauglöcher deutlich weniger Widerstand überwunden werden, als für die Durchdringung in &lt;code&gt;Z&lt;/code&gt;. Hierdurch liegen nur noch &lt;code&gt;R29&lt;/code&gt; und &lt;code&gt;R22||R21&lt;/code&gt; in Reihe. Überdeckt das Werkstück mehrere inaktive Lochfelder, geht &lt;code&gt;R22&lt;/code&gt; gegen 0.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;In der Folge werden die Widerstände vor &lt;code&gt;R19&lt;/code&gt;, nämlich &lt;code&gt;R18||(R29+(R21||R22))&lt;/code&gt; so klein, dass die Pumpe hier praktisch nur noch Umgebungsluft zieht und an der Stelle kaum Spannkraft aufbringen kann. Über &lt;code&gt;R25&lt;/code&gt; wirkt sich der Querluftstrom außerdem auf den Druckabfall von &lt;code&gt;R17, R15&lt;/code&gt; aus.&lt;/p&gt;&#10;&lt;p&gt;Jetzt wirken die kleinen Sauglöcher als Spannungsteiler, sodass an ihnen ein nicht unerheblicher Teil des Unterdrucks &amp;ldquo;hängen&amp;rdquo; bleibt.&lt;/p&gt;&#10;&lt;p&gt;Also stimmt auch der auf dem Manometer angezeigte Druck überhaupt nicht mehr mit den Gegebenheiten am Werkstück überein: Obwohl ich hier noch immer &lt;code&gt;-200mBar&lt;/code&gt; angezeigt bekomme, reduziert sich die Spannkraft genau an den für das Niederhalten empfindlichsten Stellen&lt;/p&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-resnetwork.jpg" alt="Image: Simplified equivalent circuit diagram for a vacuum clamping system" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;am Rand des Werkstücks&lt;/li&gt;&#10;&lt;li&gt;in angrenzenden Bereichen zu denen, wo das Werkstück nicht aktiv angesaugt wird&lt;/li&gt;&#10;&lt;li&gt;sowie an jeder Durchfräsung und einem gewissen Umkreis&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;auf nahe null.&lt;/p&gt;&#10;&lt;p&gt;Für alle anderen Bereiche gelten die &lt;a href="https://en.wikipedia.org/wiki/Kirchhoff%27s_circuit_laws" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Kirchhoff&amp;rsquo;schen Gesetze&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;: Gebildet wird ein Spannungsteiler bestehend aus Innenwiderstand der Pumpe, Vakuumsystem sowie dem Durchdringungswiderstand des Werkstücks. Nur die noch am Werkstück abfallende Spannung bestimmt die Niederhaltekraft. Sie ist - ich wiederhole mich jetzt - unter Umständen &lt;em&gt;viel geringer&lt;/em&gt; als der Blick auf das Manometer vermuten lässt.&lt;/p&gt;&#10;&lt;h2 id="lösungen"&gt;Lösungen&lt;/h2&gt;&#10;&lt;p&gt;Ganz schön viel Theorie für die banale Erkenntnis, dass mir das Werkstück auf dem Maschinenbett verdreht wird, oder? Folgende Lösungen habe ich für ein solches Problem im Köcher:&lt;/p&gt;&#10;&lt;h3 id="pumpe--vakuumtisch"&gt;Pumpe / Vakuumtisch&lt;/h3&gt;&#10;&lt;p&gt;Bei der Arbeit mit porösen Werkstoffen ist es wichtig, dass der Tisch selbst einen geringen Durchströmungswiderstand aufweist. Nur so fällt der Großteil des Unterdrucks am Werkstück ab, was zu höherer Spannkraft führt. Hier wäre nach dem Motto &amp;ldquo;viel hilft viel&amp;rdquo; also eine Pumpe mit mehr Durchsatz, ein Tisch mit größeren Saugöffnungen und ein Vakuumverteiler mit größeren Schlauchdurchmessern angemessen. Kommt für mich aber überhaupt nicht in Frage.&lt;/p&gt;&#10;&lt;h3 id="haftvermittler"&gt;Haftvermittler&lt;/h3&gt;&#10;&lt;p&gt;Ich könnte probieren, eine grobmaschigere Gummimatte zu verwenden. Mit einem &lt;code&gt;20mm&lt;/code&gt; Lochraster etwa verdreifacht sich der Widerstand für den Querluftstrom, sodass Außenbereiche nicht mehr so stark vom Druckverlust betroffen sind. Zudem besitzt eine solche Matte mehr Gummifläche, was die Haftreibung erhöht.&lt;/p&gt;&#10;&lt;p&gt;Klebeband statt Frischhaltefolie auf der Materialoberfläche löst das Problem des Verfangens im Fräser und verschließt lokal die Poren des Werkstoffes, kann also die Haltekraft in kritischen Bereichen verbessern.&lt;/p&gt;&#10;&lt;p&gt;Natürlich können auch auf einem Vakuumtisch gängige Methoden wie die Aufbringung doppelseitigen Klebebandes oder das Festschrauben des Werkstückes angewandt werden. Mache ich aber nicht - denn dann müsste ich mir ja eingestehen, dass mein Vakuumtisch nicht für alle Fälle geeignet ist.&lt;/p&gt;&#10;&lt;h3 id="haltestege-spannvorrichtung"&gt;Haltestege, Spannvorrichtung&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-04-02-spirofixtures.jpg" alt="Image: Fixture for holding my gears in place" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Diese Lösung zielt darauf ab, Verdrehen und Verschieben des Werkstückes zu vermeiden. Der Vakuumtisch wird hier nur noch zum Niederhalten verwendet, während eine passend angefertigte Halterung Kräfte in &lt;code&gt;XY-Richtung&lt;/code&gt; aufnimmt. Im Bild sind Halterungen für die Schablone sowie das Zahnrad zu erkennen, die ebendiese Aufgabe erfüllen. Sie werden auf dem Maschinenbett befestigt und benötigen einen präzisen Nullpunkt, um die Kanten versatzfrei brechen zu können.&lt;/p&gt;&#10;&lt;p&gt;Ich verwende eine Spielpassung mit &lt;code&gt;0.1mm&lt;/code&gt; Luft zwischen den Komponenten. Ist das Spiel kleiner, benötige ich zu viel Kraft, den Rohling einzusetzen. Setze ich größere Werte an, lassen sich die Werkstücke zu stark verdrehen.&lt;/p&gt;&#10;&lt;p&gt;Mit dieser Variante kann ich verzogene Werkstücke geradeziehen, weiterhin sehr feine Gravuren anfertigen und gehe trotzdem sicher, dass mir nichts mehr verrutscht.&lt;/p&gt;&#10;&lt;p&gt;Nachteilig sind der Zeit- und Materialaufwand für Planung und Herstellung individueller Halterungen bzw. Schablonen.&lt;/p&gt;&#10;&lt;h3 id="modifikation-des-werkstückes"&gt;Modifikation des Werkstückes&lt;/h3&gt;&#10;&lt;p&gt;Dies liegt auf der Hand: Ich kann das Werkstück in seinen Eigenschaften so verändern, dass es undurchlässiger wird für Luft. So kann ich z.B. die Unterseite mit einer Folie bekleben und damit zusätzlich die Haftreibung verbessern.&lt;/p&gt;&#10;&lt;p&gt;Wer wie ich nicht so gerne Plastik verwendet: Ich habe herausgefunden, dass Backpapier / Frischhaltepapier aus ungebleichtem Zellstoff kaum Luft hindurch lässt. Demnächst probiere ich mal aus, Materialien damit zu bekleben bzw. von Durchfräsung betroffene Bereiche damit abzudecken statt mit Kunststofffolie.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Liegt ein solches Problem regelmäßig vor, sind Rastervakuumtische mit Dichtschnur eine hervorragende Lösung: Hier mittels einer linienförmigen Dichtung an den Außenkanten des Werkstückes gegen den Umgebungsluftdruck abgedichtet, sodass Werkstückkanten keine Chance bekommen, sich hochzuwölben. Bei meinem Lochrastertisch jedoch wirkt die gesamte Fläche der Gummimatte als Dichtung, sodass die Wölbung in Form einer Kontaktlinie auf der Matte vorliegt. Linien haben jedoch keinen Flächeninhalt, und nur durch jenen kann mein Tisch niederhalten. Dies ist übrigens auch der Grund, warum biegsame Materialien wie Folien so leicht vom Vakuumtisch abgezogen werden können.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Maker Faire Ruhr 2024</title><link>https://blog.schallbert.de/makerfaire-ruhr/</link><pubDate>Mon, 25 Mar 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/makerfaire-ruhr/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-maker-faire-logo-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Maker Faire Ruhr Logo / Banner"&#10; title="Maker Faire Ruhr 2024" /&gt;&#10;&lt;h2 id="vorbereitung"&gt;Vorbereitung&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-maker-faire-logo.jpg" alt="Image: Maker Faire Logo" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Seit Anfang Januar bereiten sich zwei meiner Freunde und ich auf die &lt;a href="https://www.makerfaire-ruhr.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Maker Fair Ruhr&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; in der &lt;a href="https://www.dasa-dortmund.de/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;DASA Dortmund&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; vor. Im Zuge dessen habe ich Faserplatten furniert, Rohlinge für Gravuren ausgeschnitten, Tischdecken besorgt, PC-Gehäuse bearbeitet, Zahnräder gefräst, LED-Streifen programmiert, ein Standschild designt, eine CNC-Maschine eingerichtet, sie für den Gravurbetrieb optimiert und so weiter&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;Als der Termin dann nahte, wurde es dann doch knapp. Es geht schon eine Menge Zeit und Koordination in so eine Messevorbereitung.&lt;/p&gt;&#10;&lt;section class="hugo-gallery"&gt;&#10; &lt;div class="hugo-gallery__frame"&gt;&#10; &lt;div class="hugo-gallery__grid" role="list"&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-0" aria-label="Image: Vacuumpump and -table"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-vacuumpump.jpg" alt="Image: Vacuumpump and -table" class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-1" aria-label="Image: CNC machine, ready to run."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-hobbyline_readytorun.jpg" alt="Image: CNC machine, ready to run." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-2" aria-label="Image: Signs for my projects that I&amp;#39;ll showcase."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-standschilder.jpg" alt="Image: Signs for my projects that I&amp;#39;ll showcase." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-3" aria-label="Image: Retrogaming sign for another booth"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-retrogaming_sign.jpg" alt="Image: Retrogaming sign for another booth" class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-4" aria-label="Image: Beautiful scrap - residue piece of PMMA in form of a wave"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-pmma_wave.jpg" alt="Image: Beautiful scrap - residue piece of PMMA in form of a wave" class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-5" aria-label="Image: The side panel of a midi-tower PC: now with a window"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-pcwindow_sea.jpg" alt="Image: The side panel of a midi-tower PC: now with a window" class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-6" aria-label="Image: Schallbert&amp;#39;s booth on Maker Faire Ruhr 2024"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-DerStand.jpg" alt="Image: Schallbert&amp;#39;s booth on Maker Faire Ruhr 2024" class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-7" aria-label="Image: 2000s-retro-PCs set up and ready to play."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-Gaming.jpg" alt="Image: 2000s-retro-PCs set up and ready to play." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-8" aria-label="Image: CNC machine detail view"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-StandDetail.jpg" alt="Image: CNC machine detail view" class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-9" aria-label="Image: Visitors on Saturday"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-Besucher.jpg" alt="Image: Visitors on Saturday" class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-10" aria-label="Image: Schallbert&amp;#39;s booth, rear view"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-Backstage.jpg" alt="Image: Schallbert&amp;#39;s booth, rear view" class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-11" aria-label="Image: Many more visitors on Sunday."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-Menschenmenge.jpg" alt="Image: Many more visitors on Sunday." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;/div&gt;&lt;div class="hugo-gallery__caption"&gt;Impressionen von der Maker Faire Ruhr 2024.&lt;/div&gt;&lt;/div&gt;&#10;&lt;/section&gt;&#10;&#10; &lt;div class="hugo-gallery__full" aria-live="polite"&gt;&lt;figure id="gallery-full-0" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-vacuumpump.jpg" alt="Image: Vacuumpump and -table" class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Hier wird der Vakuumtisch in Betrieb genommen und getestet.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-1" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-hobbyline_readytorun.jpg" alt="Image: CNC machine, ready to run." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Die CNC-Maschine für die Messe. Fertig eingerichtet und betriebsbereit.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-2" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-standschilder.jpg" alt="Image: Signs for my projects that I&amp;#39;ll showcase." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Selbstgefräster Schilder für die dargestellten Projekte.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-3" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-retrogaming_sign.jpg" alt="Image: Retrogaming sign for another booth" class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Das Standschild für einen guten Freund, der Retro-PCs und -Spiele ausstellt.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-4" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-pmma_wave.jpg" alt="Image: Beautiful scrap - residue piece of PMMA in form of a wave" class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Schöner Abfall - ein Reststück Acrylglas in Form einer Welle&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-5" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-pcwindow_sea.jpg" alt="Image: The side panel of a midi-tower PC: now with a window" class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Das Gegenstück zur Welle: Ein PC mit einzigartigem Fenster, selbstgefräst.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-6" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-DerStand.jpg" alt="Image: Schallbert&amp;#39;s booth on Maker Faire Ruhr 2024" class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Die LED-Streifen sind fertig programmiert und alles steht an seinem Platz.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-7" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-Gaming.jpg" alt="Image: 2000s-retro-PCs set up and ready to play." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Retro-2000er-PCs sind aufgebaut und vernetzt. Bereit zum Spielen.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-8" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-StandDetail.jpg" alt="Image: CNC machine detail view" class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Die betriebsbereite CNC-Maschine auf der Messe.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-9" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-Besucher.jpg" alt="Image: Visitors on Saturday" class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Bereits einige Besucher, Maker und Interessierte am Samstag.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-10" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-Backstage.jpg" alt="Image: Schallbert&amp;#39;s booth, rear view" class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Backstage des betriebsbereiten Standes.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-11" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-Menschenmenge.jpg" alt="Image: Many more visitors on Sunday." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Doch am Sonntag war noch viel mehr los.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;/section&gt;&#10;&lt;h3 id="projekte"&gt;Projekte&lt;/h3&gt;&#10;&lt;p&gt;Auf der Messe wollte ich ein paar meiner schönsten Projekte vorstellen wie etwa den &lt;a href="https://blog.schallbert.de/projects/seifenbutler/"&gt;Seifenbutler&lt;/a&gt;, meine übergroßen &lt;a href="https://blog.schallbert.de/projects/spirograph/"&gt;Spirographen&lt;/a&gt; und Besuchern ermöglichen, eigene Schilder &lt;a href="https://blog.schallbert.de/negative-carving-with-estlcam/"&gt;graviert zu bekommen&lt;/a&gt;, nach Bedarf sogar mit &lt;a href="https://blog.schallbert.de/projects/qr-codengrave/"&gt;QR-Code&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Damit die Fräsarbeiten schnell von der Hand gehen, habe ich Rohlinge gefertigt, die später nur noch graviert werden mussten. Die Spirographen musste ich wegen der erforderlichen doppelseitigen Bearbeitung ebenfalls vorab fertigen.&lt;/p&gt;&#10;&lt;h3 id="maschinen"&gt;Maschinen&lt;/h3&gt;&#10;&lt;p&gt;Ich konnte ein paar Firmen davon überzeugen, mir Maschinen und -Teile für die Messe zu leihen:&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/1year_cnc/sorotec.jpg" alt="Bild: Sorotec Logo" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Von &lt;a href="https://www.sorotec.de/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Sorotec&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; borgte ich mir eine &amp;ldquo;Hobbyline4530&amp;rdquo;, deren kleinste und leichteste Maschine.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/1year_cnc/stritzelberger.jpg" alt="Bild: Stritzelberger Logo" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Die Firma &lt;a href="https://vakuumtisch.de/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Stritzelberger&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; stellte mir einen zur Maschine passenden Vakuumtisch inkl. passender Pumpe zur Verfügung.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/1year_cnc/eding.jpg" alt="Bild: EdingCNC Logo" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;a href="https://edingcnc.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;EdingCNC&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; unterstützte bei der Einrichtung ihrer Software, lieh mir Aussteller für die aktuellste Steuerungshardware und kam schließlich sogar live an meinem Messestand zwecks Austausch vorbei.&lt;/p&gt;&#10;&lt;h3 id="das-standschild"&gt;Das Standschild&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-23-schallbert-standschild.jpg" alt="Image: My plan for a great booth sign" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Für das Standschild habe ich mir etwas ganz Besonderes ausgedacht: Ein Planetengetriebe! Drei bewegliche Zahnräder umkreisen hierbei ein feststehendes Sonnenrad. Um das Standschild aufhängen und das Getriebe dabei randlos behalten zu können, kam für die Planetenträger keine mittig gelagerte Achse in Frage.&lt;/p&gt;&#10;&lt;p&gt;Diese Aufgabe übernimmt ein durch das Sonnenrad verdecktes, durch drei Kugellager (Bild: Im 120°-Winkel angeordnet, montiert auf den Zähnen des Sonnenrads) gehaltenes Hohlrad. Es wird von einem Schrittmotor mit der Untersetzung 1:5 angetrieben. Das Hohlrad bietet Aufnahmen für die Planetenträger. So bekomme ich den mittleren Bereich des Sonnenrades frei und kann dort sowohl Antriebsmotor als auch Beleuchtung und Schildhalterungen unterbringen.&lt;/p&gt;&#10;&lt;p&gt;Ein auf meinem PeerTube-Kanal befindliches Video zeigt, wie ich das Standschild mit meiner CNC-Portalfräse fertige.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/xsLhWWygyJbtJGwUGrKvVE" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Herstellung Standschild&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="verzögerungen"&gt;Verzögerungen&lt;/h3&gt;&#10;&lt;p&gt;Wie immer bei solchen größeren Unternehmungen ging bei den Vorbereitungen auch einiges schief:&lt;/p&gt;&#10;&lt;h4 id="ablösung-des-vakuums-bei-der-fertigung-der-spirographen"&gt;Ablösung des Vakuums bei der Fertigung der Spirographen&lt;/h4&gt;&#10;&lt;p&gt;Die Spirographen wollte ich aus furniertem MDF herstellen, da das Material sehr schön anzusehen und gleichzeitig stabil genug ist, dass es auch bei rauer Behandlung keinen Schaden nimmt. Also habe ich &lt;code&gt;10mm&lt;/code&gt; starke mitteldichte Faserplatte mit Olivenholzfurnier beklebt (zeitraubend!) und es dann auf meiner Portalfräse beareitet.&lt;/p&gt;&#10;&lt;p&gt;Da ich ein &lt;a href="https://blog.schallbert.de/why-vacuum-table/"&gt;Vakuumspannsystem&lt;/a&gt; verwende, ist es total wichtig, einen Mindestunterdruck zu erreichen, da mir sonst die Werkstücke nicht an Ort und Stelle bleiben. Nun ist MDF luftdurchlässig, sodass die Pumpe selbst bei noch nicht bearbeiteter Platte gut zu tun hat. Die Geometrie meines Entwurfs für die Spirographen ist nun leider so, dass die für das Spannen verbliebene Oberfläche bei der Bearbeitung immer weiter sinkt.&lt;/p&gt;&#10;&lt;p&gt;In letzter Konsequenz hatte ich das Problem, dass sich die Zahnräder spätestens bei der Kantenbearbeitung der Rückseite mitdrehen wollen. Sogar meine verzweifelten Versuche genügend Unterdruck aufrecht zu erhalten, indem ich großflächig Frischhaltefolie auf dem Werkstück aufbrachte, scheiterten.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-03-25-spirograph.jpg" alt="Image: Spirograph under test" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;So erhielt ich anstatt der geplanten zehn Spirographen nur vier - die Restlichen waren Ausschuss. &lt;em&gt;Merke&lt;/em&gt;: MDF ist auf meinem Vakuumspannsystem nur für großflächige Werkstücke einsetzbar.&lt;/p&gt;&#10;&lt;h4 id="problematische-zahnradpaarung-beim-spirographen"&gt;Problematische Zahnradpaarung beim Spirographen&lt;/h4&gt;&#10;&lt;p&gt;Der erste Spirograph aus MDF war zudem leider gar nicht richtig zu gebrauchen: Ständig verklemmte sich ein Zahn auf der Flanke des Hohlrades und weigerte sich, in Eingriff zu gehen. Die Ursachen dafür liegen darin, dass ich kein Flankenspiel vorgesehen und die Zahnköpfe ohne Radius gefertigt hatte.&lt;/p&gt;&#10;&lt;p&gt;In der Folge muss das Zahnrad immer kräftig gegen das Hohlrad gedrückt werden, um den nächsten Zahneingriff zu ermöglichen. Rutscht das Zahnrad nur ein paar Zehntelmillimeter heraus, so blockiert es. &lt;em&gt;Abhilfe&lt;/em&gt;: Sowohl Zahnrad- als auch Hohlradzahnköpfe mit &lt;code&gt;r=3.0mm&lt;/code&gt; versehen und die Zahnradpaarung mit Flankenspiel von &lt;code&gt;a=0.25mm&lt;/code&gt; konstruieren.&lt;/p&gt;&#10;&lt;h4 id="falsch-gravierte-schriften-auf-dem-großen-standschild"&gt;Falsch gravierte Schriften auf dem großen Standschild&lt;/h4&gt;&#10;&lt;p&gt;Hier habe ich mich sogar zweimal vertan und damit leider viel Ausschuss produziert. Beim ersten Versuch hatte ich übersehen, das Zahnrad für die rückseitige Bearbeitung zu spiegeln. Ich dachte, das sei nicht nötig, weil das Zahnrad selbst spiegelsymmetrisch ist. Leider waren die Haltestifte aber nicht spiegelsymmetrisch eingelassen, sodass sich die Rückseite leider nicht in Deckung mit der Vorderseite befand.&lt;/p&gt;&#10;&lt;p&gt;Beim zweiten Versuch - siehe Video oben - hatte ich dies zwar behoben, jedoch vergessen, den Nullpunkt ensprechend der neuen Zentrierstiftlage auf der Rückseite anzupassen. Zum Glück habe ich das während der Bearbeitung bemerkt und konnte korrigieren.&lt;/p&gt;&#10;&lt;h4 id="retro-pc-fensterfertigung"&gt;Retro-PC Fensterfertigung&lt;/h4&gt;&#10;&lt;p&gt;Bei einem der drei Retro-PCs habe ich die Gehäuserückseite um 180° gedreht aufs Maschinenbett gelegt. Das Fenster wurde also &amp;ldquo;verkehrt herum&amp;rdquo; gefräst, was ich erst nach Beendigung des Fräsjobs bemerkte. Zum Glück konnte mein Freund durch Feilen und Biegen der Rastnasen dafür sorgen, dass die Gehäuserückseite auch &amp;ldquo;verkehrt herum&amp;rdquo; am PC angebracht werden konnte.&lt;/p&gt;&#10;&lt;p&gt;Bei der Fertigung des letzten Fensters verhakte sich der Spanschutz im Gehäusefalz und brach, da ich die Maschine nicht rechtzeitig zum Stehen bekam. Dies konnte überhaupt nur passieren, weil sich der Spanschutz durch die Vibrationen bei der Bearbeitung lockerte. &lt;em&gt;Merke&lt;/em&gt;: Die Fräse ist kein Spielzeug. Also keine Kompromisse machen oder halbgare Provisorien anbringen!&lt;/p&gt;&#10;&lt;h2 id="die-maker-faire-ruhr-2024"&gt;Die Maker Faire Ruhr 2024&lt;/h2&gt;&#10;&lt;p&gt;Die Messe selber war super. Ich wurde sehr freundlich empfangen und seitens der DASA erhielt ich hervorragende Unterstützung beim Ausladen und aller weiterer Logistik. Auch der mir zugeteilte Stand war toll: Sehr groß, gut gelegen, mit ausreichend Steckdosen - was will man mehr.&lt;/p&gt;&#10;&lt;p&gt;Ich hatte eine Firma zu Besuch an meinem Stand, Familie und Freunde waren gekommen und sogar einige Leute aus den CNC-Foren sowie ein paar Arbeitskollegen waren extra angereist. Mehr kann ich mir nicht wünschen.&lt;/p&gt;&#10;&lt;h3 id="aufbau"&gt;Aufbau&lt;/h3&gt;&#10;&lt;p&gt;Beim Ausladen kam uns zum Glück ein Stapler zur Hilfe.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2024-03-25-unloading.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Unloading the CNC&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Alles Weitere war Handarbeit und nicht deutlich anders, als für ein Musikkonzert aufzubauen: Tische rücken, Stromleitungen legen, Lichtanlage montieren, Ausstellungsstücke aufbauen.&lt;/p&gt;&#10;&lt;h3 id="besucher"&gt;Besucher&lt;/h3&gt;&#10;&lt;p&gt;Für meine erste Maker Faire kann ich eine positive Bilanz ziehen: Es waren echt viele Leute an unserem Stand und ich hatte kaum ein paar Minuten zum Ausruhen - ständig gab es etwas zu erzählen, meine Projekte vorzustellen oder über die ausgestellte Maschine zu sprechen. Aber genau das war es ja, was uns an diesem Format gereizt hat. Außerdem hat die Technik funktioniert, die Maschine lief problemlos und alle Dinge, die ich so lange vorher vorbereitet hatte (wie etwa das Standschild), konnten ausgestellt bzw. im Betrieb gezeigt werden.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2024-03-25-short_engrave.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;QR-Codengrave Demonstration&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="was-ich-beim-nächsten-mal-trotzdem-anders-machen-würde"&gt;Was ich beim nächsten Mal trotzdem anders machen würde&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Die Maker Faire ist vom Setup her keine Verkaufsmesse, sondern ein riesiges Show &amp;amp; Tell. Daher würde ich nicht nochmal einen kostenpflichtigen Verkaufsstand buchen, denn das hat sich in meinem Falle nicht gelohnt.&lt;/li&gt;&#10;&lt;li&gt;Ein bis zwei Personen mehr für die Standbetreuung wären prima. Dann könnte man sich mal ablösen.&lt;/li&gt;&#10;&lt;li&gt;Mehr Material für die Spirographen! Die Stifte waren am Ende der Veranstaltung komplett durch und Papier hatten wir schon Mitte des zweiten Tages keines mehr.&lt;/li&gt;&#10;&lt;li&gt;Mehr Vorträge / Demos. Die Fräse lief zwar schon oft, aber es mangelte dann doch an Vorlagen und Wünschen seitens der Besucher. Beim nächsten Mal werde ich mehr eigene Designs mitnehmen und auch ohne Anfrage zu Demozwecken fertigen.&lt;/li&gt;&#10;&lt;li&gt;Bessere Verweise auf die Internetpräsenz vorbereiten, denn ein QR-Code alleine hilft da nicht allen weiter.&lt;/li&gt;&#10;&lt;li&gt;Mehr Kaffee: Die Versorgung mit Heißgetränken (und jetzt wo ich es schreibe, auch die mit Nahrung) war unterdimensioniert und leider auch nicht so schmackhaft, wie ich mir das gewünscht hätte. Also besser selbst Stullen schmieren und Thermoskannen mitbringen.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Seifenbutler - Nullserie!</title><link>https://blog.schallbert.de/seifenbutler-pilot-run/</link><pubDate>Thu, 08 Feb 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/seifenbutler-pilot-run/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-08-seifenbutler-white.jpg"&#10; class="post-cover"&#10; alt="Image: Seifenbutler ready for sale"&#10; title="Seifenbutler - Nullserie!" /&gt;&#10;&lt;p&gt;Jetzt ist mein Hardware-Projekt &lt;a href="https://blog.schallbert.de/projects/move-blog-to-own-server/"&gt;Seifenbutler&lt;/a&gt; abgeschlossen. Nach langer Entwicklungszeit habe ich einige Exemplare in weiß, schwarz, transparent und opalweiß hergestellt und sie in einem ersten &lt;a href="https://mopemaster.com/produkt/seifenbutler-seifenhalter-dusche/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Online-Shop&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; platzieren lassen können.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-08-seifenbutler-collection.jpg" alt="Image: Seifenbutler in three different colours" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="wie-bestimme-ich-den-verkaufspreis"&gt;Wie bestimme ich den Verkaufspreis?&lt;/h2&gt;&#10;&lt;p&gt;Da ich hochwertige Materialien verwende und die Fertigungs- sowie Montagezeit lang ausfällt, habe ich bei der Preisgestaltung nicht so viel Luft nach unten. Andererseits kann ich für einen Gebrauchsgegenstand wie den Seifenbutler keine Mondpreise aufrufen - es sei denn, ich verwendete edle Hölzer oder andere, ausgefallene oder aufwändig zu bearbeitende Materialien. Für solche Kalkulationen gibt es eine Menge Material online. Ich habe mich dafür an einem &lt;a href="https://www.youtube.com/watch?v=mxwggzhe_hs" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Youtube-Video des Kanals &amp;ldquo;Cutting It Close&amp;rdquo;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; orientiert.&lt;/p&gt;&#10;&lt;p&gt;Meine Preisberechnung setzt sich aus folgenden Posten zusammen:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;Endpreis = Materialpreis&#10; + Maschinenzeit (Investition, Verschleiß, Werkzeuge, Verbrauchsmaterialien)&#10; + Fertigungs- und Montagezeit &#10; + Verpackungskosten &#10; + Gebühren (Porto, Zahlungsdienstleister, Shops)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wie zu erkennen ist, habe ich meine eigene Zeit für die Produktentwicklung da überhaupt nicht eingerechnet - müsste ich dies tun, so würden ganz locker noch einmal der Gegenwert zu &lt;code&gt;250h&lt;/code&gt; Arbeitszeit hinzukommen.&lt;/p&gt;&#10;&lt;h3 id="materialpreise"&gt;Materialpreise&lt;/h3&gt;&#10;&lt;p&gt;&lt;strong&gt;Stand Ende 2023&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Ok, dann listen wir mal schnell die Auslagen pro Teil auf:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Sückzahl&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Artikel&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Preis/Einheit&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Verwendete Einheit&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Endpreis&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;PMMA 5mm XT&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;90€/m²&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,03&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2,70€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Rändel M4 A2 22mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;7,54€/25St&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,04&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,30€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Scheibe M4 A2 12mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,94€/100St&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,01&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,01€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Stift M4 A2 12mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6,70€/50St&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,02&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,13€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schraube M4 A2 8mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12,40€/200St&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,025&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,31€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Materialpreis/Teil&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3,45€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="maschinenzeit"&gt;Maschinenzeit&lt;/h3&gt;&#10;&lt;p&gt;Die Maschinenzeit setze ich für dieses Projekt mit &lt;code&gt;100€/h&lt;/code&gt; an. Der Löwenanteil sind hierbei Investitions-, Wartungs-, sowie Verschleißkosten der Maschine mit &lt;code&gt;50€/h&lt;/code&gt; plus meiner eigenen Arbeitszeit, die ich einfach mal mit &lt;code&gt;30€/h&lt;/code&gt; ansetze.&lt;/p&gt;&#10;&lt;p&gt;Die Werkzeugkosten liegt mit ca. &lt;code&gt;15€/h&lt;/code&gt; recht hoch, da ich vier verschiedene Werkzeuge, allesamt mit kleinem Durchmesser und damit entsprechen hohem Verschleiß benutzen muss.&lt;/p&gt;&#10;&lt;p&gt;Verbrauchsmaterialien sind Vakuumvliese, Positionierstifte, Schnittschutzhandschuhe und Klebeband. Letzeres benötige ich für die Fixierung der kleinen Frästeile sowie der Spirale. Ihr Anteil an den Maschinenzeitkosten liegt bei etwa &lt;code&gt;5€/h&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Die Maschinenzeit beträgt momentan &lt;code&gt;8min/Teil&lt;/code&gt;. Somit beläuft sich Posten &lt;code&gt;Maschinenzeit&lt;/code&gt; auf &lt;code&gt;13,30€/Stück&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="montagezeit"&gt;Montagezeit&lt;/h3&gt;&#10;&lt;p&gt;Für die Montage eines Seifenhalters benötige ich etwa 5 Minuten. Lege ich einen Stundensatz von &lt;code&gt;30€&lt;/code&gt; zugrunde, so komme ich hier auf &lt;code&gt;2,50€/Stück&lt;/code&gt;.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-02-08-seifenbutler-assembly.jpg" alt="Image: Seifenbutler ready to assemble" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="verpackungskosten-und-materialeinkauf"&gt;Verpackungskosten und Materialeinkauf&lt;/h3&gt;&#10;&lt;p&gt;Ich verpacke die Seifenhalter in einfache DIN A6-Briefumschläge, nachdem ich sie sorgfältig in Packpapier eingewickelt habe. Die Kosten belaufen sich hier auf lediglich &lt;code&gt;0,20€/Stück&lt;/code&gt;. Rechne ich nun jedoch meine Arbeitszeit für Verpackung und Versandaufkleber mit ein, liegen wir bereits bei &lt;code&gt;0,90€/Stück&lt;/code&gt;. Hinzu kommen Aufwendungen für Porto beim Materialeinkauf und Kosten für Papier und Druck der Bedienungsanleitung, die ich mit weiteren &lt;code&gt;0,50€/Stück&lt;/code&gt; berechne - hier schlagen die geringen gefertigten Mengen voll durch.&lt;/p&gt;&#10;&lt;h3 id="gebühren"&gt;Gebühren&lt;/h3&gt;&#10;&lt;p&gt;Gebühren fallen z.B. für Versand- und Zahlungsdienstleister an. Hier variieren die Preise je nach Anbieter, sodass ich einen Versandaufschlag von &lt;code&gt;3,50€&lt;/code&gt; (Versand per Großbrief) einplane - dieser entfällt bei Verkäufen &amp;ldquo;an der Theke&amp;rdquo;.&lt;/p&gt;&#10;&lt;h2 id="der-gesamtpreis"&gt;Der Gesamtpreis&lt;/h2&gt;&#10;&lt;p&gt;Summiere ich alle Posten zusammen, so müsste ich einen Ladenpreis von &lt;code&gt;20,15€&lt;/code&gt; (bei Barzahlung) für die unverpackte Ware und &lt;code&gt;24,75€&lt;/code&gt; im Versandfalle mit bargeldloser Zahlung aufrufen. Und dann wäre in der Firma noch immer kein Cent Gewinn gemacht.&lt;/p&gt;&#10;&lt;p&gt;Ich bin mir nicht sicher, ob sich der Seifenbutler für solche Preise verkaufen lässt. Wenn man sich aber ehrlich macht und am Ende nicht draufzahlen will, sind solche Rechnungen zumindest überschlagsweise unumgänglich.&lt;/p&gt;&#10;&lt;h2 id="der-angenommene-kaufmann-mit-ladengeschäft"&gt;Der angenommene Kaufmann mit Ladengeschäft&lt;/h2&gt;&#10;&lt;p&gt;Wäre ich nun Kaufmann&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;, so müsste ich noch Mehrwertsteuer aufschlagen und würde auch die Entwicklungskosten irgendwie amortisiert sehen wollen. Ich bin mir auch nicht sicher, ob ich &amp;ldquo;als Kaufmann&amp;rdquo; mit einem Stundensatz von 30€ durch die Tür käme.&lt;/p&gt;&#10;&lt;p&gt;Puh. Rechnet man jetzt noch Dinge wie Ladenmiete, Kosten für Lagerfläche, Werkstattmiete, Strom, Abfallentsorgung, Mitgliedschaft beim &amp;ldquo;Grünen Punkt&amp;rdquo; nach Verpackungsgesetz, Beiträge für IHK, Berufsgenossenschaft sowie Steuern und Abgaben mit ein, so muss selbst bei einem weiteren Preisaufschlag von 20% schon ein ganzer Haufen Seifenhalter verkauft werden, um am Ende nicht ärmer dazustehen als vorher!&lt;/p&gt;&#10;&lt;p&gt;Nehmen wir mal an, ich kann mich in eine Werkstatt einmieten und einen winzigen Laden eröffnen.&lt;/p&gt;&#10;&lt;h3 id="ein-fiktiver-laden"&gt;Ein fiktiver Laden&lt;/h3&gt;&#10;&lt;p&gt;Überschlagen wir mal kurz mit ein paar angenommenen Werten:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Posten&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Preis/Einheit&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Verwendet&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Endpreis&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Laden&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;16€/m² u. Monat&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;40m²&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;640€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lagerfläche&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4€/m² u. Monat&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;8m²&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;32€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Werkstatt&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;8€/m² u. Monat&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;30m²&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;240€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Nebenkosten&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;pro Monat&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;200€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Abgaben&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;IHK / BG / Vers.&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;125€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Gebühren&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;20€/Monat&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;60€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Summe&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1297€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Personal&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1600€&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Und würde ich in diesem Laden nun nochmal für 20h/Woche jemanden für den Verkauf anstellen, entstünden hier überschlagsweise 1600€ an Kosten.&lt;/p&gt;&#10;&lt;h3 id="summa-summarum"&gt;Summa summarum&lt;/h3&gt;&#10;&lt;p&gt;Ok, fassen wir zusammen: Ich gehe mit einem Aufschlag von 20% auf die Herstellungskosten ins Rennen. Als angenommener Kaufmann müsste ich allerdings Mehrwertsteuer abführen, die ich erneut aufzuschlagen hätte.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;Ladenpreis = Herstellungspreis &#10; + Deckungsbeitrag (20%)&#10; + Mehrwertsteuer (19%)&#10; = (1,2 * 20,15€) * 1,19 = 28,77€&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Oha, damit liegen wir schon bei fast dreißig Euro pro Teil.&lt;/p&gt;&#10;&lt;p&gt;Wie viele Seifenbutler müssen also pro Monat über die Ladentheke gehen, bevor ich als Kaufmann mit Ladengeschäft Gewinn machen würde?&#10;Der Deckungsbeitrag liegt bei &lt;code&gt;4,03€&lt;/code&gt; (20% des Herstellungspreises). Wenn ich jetzt die durch Produktion und Verkauf erzeugten Kosten durch diesen Betrag teile, erhalte ich meine Mindestverkaufsmenge: &lt;code&gt;719 Stück/Monat&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Wie viele Wochenstunden müsste ich nun also mindestens an meiner CNC-Fräse stehen bzw. Seifenbutler zusammenbauen und verpacken?&#10;Nehme ich die Werte von oben, so liege ich bei insgesamt &lt;code&gt;8 + 5 + 2 = 15min/Teil&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Bei der Mindestverkaufsmenge würde mir das eine Wochenarbeitszeit von etwa &lt;code&gt;45h&lt;/code&gt; bescheren!&#10;Ach, 60h pro Woche gehen doch auch locker, oder?! Somit könnte ich 960 Stück pro Monat herstellen - und angenommen, sie verkaufen sich hervorragend, erzielte ich &lt;code&gt;971€&lt;/code&gt; extra zur Amortisation der Entwicklungskosten (Überschlag 250h*30€ = 7.500€). Knaller!&lt;/p&gt;&#10;&lt;p&gt;Ich würde dann mit der Unternehmung nach nur acht Monaten (mit je 60h/Woche, ist klar) in die Gewinnzone marschieren. Oh, oder muss man etwa noch Steuern entrichten? Und wann war noch gleich meine Maschine abbezahlt?&lt;/p&gt;&#10;&lt;h2 id="fazit-zur-wirtschaftlichkeit"&gt;Fazit zur Wirtschaftlichkeit&lt;/h2&gt;&#10;&lt;p&gt;Mir ist klar, dass die Rechnung nicht so einfach zu machen ist. Einige Parameter lassen sich nicht beliebig und linear hoch- oder herunterskalieren und z.B. die Maschinenkosten kann ich gar nicht realistisch abschätzen, weil mir schlicht Erfahrungswerte im Dauerbetrieb fehlen, Stichwort Abnutzung.&lt;/p&gt;&#10;&lt;p&gt;Dennoch genügt mir die meinerseits zu erbringende Arbeitszeit um festzuhalten:&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Das wird nix. Nettes Projekt, lohnt sich aber nicht.&lt;/strong&gt; Jedenfalls nicht aus Eigenmitteln und allein in Eigenregie. Es bleibt bei einer Nullserie und bei meiner Freude über ein gelungenes, funktionales Produkt.&lt;/p&gt;&#10;&lt;p&gt;Glücklicherweise ist das alles hier &amp;ldquo;nur mein Hobby&amp;rdquo;, sodass ich die desaströse Bilanz nicht gezwungen bin zu ziehen 😉&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Obacht. Meine Berufung ist nicht Händler oder Kaufmann, sondern das Ingenieur-dasein. Daher habe ich nur sehr rudimentäre Kenntnis von Betriebswirtschaft (dieses eine Semester damals im Studium eben, wo niemand so recht zuhören mochte). Alle meine Ausführungen hier sind Überschlagsrechnungen. Sie sollten also mit großer Vorsicht genossen werden. Ich freue mich allerdings sehr über Korrekturzuschriften und konstruktive Kritik!&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>'redir' im Caddyserver</title><link>https://blog.schallbert.de/migrating-to-subdomain/</link><pubDate>Tue, 30 Jan 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/migrating-to-subdomain/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-01-30-migrating-to-subdomain-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Thumbnail of correctly rendered &amp;#39;&amp;#39;website under construction&amp;#39;&amp;#39; page on schallbert.de"&#10; title="&amp;#39;redir&amp;#39; im Caddyserver" /&gt;&#10;&lt;p&gt;Als ich meine Website weg von Github Pages auf einen eigenen Server &lt;a href="https://blog.schallbert.de/projects/move-blog-to-own-server/"&gt;umzog&lt;/a&gt;, hatte ich einige Probleme und offene Fragen bezüglich automatischer Weiterleitungen. Mein Blog ging nämlich von der root-Domain in eine Subdomain (blog.schallbert.de) über.&lt;/p&gt;&#10;&lt;h2 id="anforderungen-an-die-weiterleitung"&gt;Anforderungen an die Weiterleitung&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Bereits auf meine Blogartikel unter altem Namen referenzierte Links z.B. auf &lt;a href="https://blog.schallbert.de/about/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;schallbert.de/about&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; sollen korrekt auf die Subdomain weiterleiten (kein &lt;em&gt;404&lt;/em&gt;)&lt;/li&gt;&#10;&lt;li&gt;Die Stammseite unter &lt;code&gt;schallbert.de&lt;/code&gt; soll vollständig nutzbar sein&lt;/li&gt;&#10;&lt;li&gt;Daten und Dateien für Domain und Subdomains sollen unabhängig voneinander sein&lt;/li&gt;&#10;&lt;li&gt;Die Webserver sollen für verschiedene Unterseiten unterschiedliche Technologien verwenden können&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="umsetzung"&gt;Umsetzung&lt;/h2&gt;&#10;&lt;p&gt;Zuerst hatte ich gedacht, dass ich die Weiterleitungen im DNS-provider über &lt;code&gt;A&lt;/code&gt; und &lt;code&gt;CNAME&lt;/code&gt; Einträge erreichen könnte. Schnell wurde mir jedoch klar, dass ich &lt;em&gt;a)&lt;/em&gt; keine Ahnung hatte, wie DNS überhaupt funktioniert und &lt;em&gt;b)&lt;/em&gt; der richtige Weg über die Konfiguration meines Webservers führt.&lt;/p&gt;&#10;&lt;h3 id="erster-versuch-redir-bei-"&gt;Erster Versuch: redir bei &amp;lsquo;/&amp;rsquo;&lt;/h3&gt;&#10;&lt;p&gt;Auf meinem Webserver (Caddy) werden Weiterleitungen in der &lt;code&gt;Caddyfile&lt;/code&gt; per &lt;code&gt;redir&lt;/code&gt; spezifiziert. Dabei kann das &lt;code&gt;permanent&lt;/code&gt; (als sogenannte http &lt;code&gt;301&lt;/code&gt;) oder &lt;code&gt;temporary&lt;/code&gt; per &lt;code&gt;302&lt;/code&gt; (Standardeinstellung) geschehen.&lt;/p&gt;&#10;&lt;p&gt;Mein erster Gedanke war, dass ich &lt;code&gt;schallbert.de&lt;/code&gt; unmodifiziert lasse und alles hinter einem eventuellen &lt;code&gt;/&lt;/code&gt; weiterleite. Dies sieht dann in der Caddyfile wie folgt aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;schallbert.de {&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Define webserver&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;root * /www/landing&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;encode gzip&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;file_server&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Redir to subdomain if domain has a slash&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@redirect path_regexp /*&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;redir @redirect https://blog.schallbert.de{uri}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In der Folge hat Caddy schlicht alles an die Subdomain weitergeleitet. Der Haken dabei war, dass die Subdomain selbst ja auch wieder Slashes bzw. eine URI hinter dem Slash enthält und dann wiederum weitergeleitet wird. Beispiel:&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;schallbert.de/post -&amp;gt; blog.schallbert.de/post -&amp;gt; blog.schallbert.de/post -&amp;gt; blog.schallbert.de/post -&amp;gt; [...]&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Somit bekam ich eine rekursive Endlosweiterleitung. Ich habe meinen Server damit komplett lahmgelegt 😅&lt;/p&gt;&#10;&lt;h3 id="zweiter-versuch-filterung-per-regexp"&gt;Zweiter Versuch: Filterung per Regexp&lt;/h3&gt;&#10;&lt;p&gt;Ich brauche also eine bessere Filterung, sodass nur Slashes mit weiteren Zeichen dahinter auch wirklich weitergeleitet werden und die Weiterleitung nicht rekursiv ablaufen kann. Zum Glück hatte ich Hilfe von einem guten Freund, sodass kurze Zeit darauf folgendes &lt;code&gt;Caddyfile&lt;/code&gt; entstand:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;schallbert.de {&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Define webserver&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;root * /www/landing&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;encode gzip&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;file_server&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Redir to subdomain to maintain links for blog&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@redirect path_regexp ^/[^/]+(/.*)?$&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;redir @redirect https://blog.schallbert.de{uri}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Die Regexp prüft ab, ob ein Slash hinter der Domain URL vorliegt und dahinter zumindest ein weiteres Zeichen folgt. Hier funktioniert die Weiterleitung nun wie gewünscht und mein Blog ist auch unter den alten Links weiterhin vollständig erreichbar.&lt;/p&gt;&#10;&lt;p&gt;Trotzdem war ich noch nicht ganz zufrieden. Denn als ich mir eine schöne &amp;ldquo;Landing Page&amp;rdquo; auf meiner Root Domain erstellte, war von ihr plötzlich nur noch das reine HTML zu sehen. Kein favicon, keine Bilder, kein CSS.&lt;/p&gt;&#10;&lt;p&gt;Ein kurzer Blick in die Entwickleroptionen brachte schnell die Erkenntnis dass die Pfade aller Assets, die ich auf meiner Landing Page hinterlegt hatte, durch die Ordnerstruktur mit &lt;code&gt;/&lt;/code&gt; gekennzeichnet, an die Subdomain weitergeleitet wurden. Wo sie natürlich nicht liegen.&lt;/p&gt;&#10;&lt;h3 id="dritter-versuch-regexp-plus-dateisuche"&gt;Dritter Versuch: Regexp plus Dateisuche&lt;/h3&gt;&#10;&lt;p&gt;Zum Glück bin ich wieder mal mit dem Problem nicht allein, sodass im &lt;a href="https://caddy.community/t/redirect-if-file-not-present/7902" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Forum von Caddy&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; die Lösung zu finden war:&lt;/p&gt;&#10;&lt;p&gt;Mit dem &lt;code&gt;not file&lt;/code&gt; Marker kann ich in der &lt;code&gt;Caddyfile&lt;/code&gt; Weiterleitungen definieren, falls Ressourcen auf der aktuellen Seite unauffindbar sind.&lt;/p&gt;&#10;&lt;p&gt;Somit sieht meine Lösung jetzt wie folgt aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;schallbert.de {&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@filenotfound {&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;path_regexp ^/[^/]+(/.*)?$&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;not file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# show Caddy where to find page resources&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;root * /www/landing&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;encode gzip&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;route {&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Redirect to subdomain if article cannot found on root&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;redir @filenotfound https://blog.schallbert.de{uri}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# else, define webserver and show page&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;file_server&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hier ist wichtig, dass ich die Seite mit &lt;code&gt;root * /www/&amp;lt;pagelocation&amp;gt;&lt;/code&gt; definiere, bevor ich die &lt;code&gt;route&lt;/code&gt; anlege. Denn sonst werden die Assets erst verfügbar, nachdem ich bereits aus Ermangelung vorhandener Dateien weitergeleitet habe.&lt;/p&gt;&#10;&lt;h2 id="erfolg"&gt;Erfolg!&lt;/h2&gt;&#10;&lt;p&gt;Jetzt endlich wird meine Baustellenseite korrekt dargestellt.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-01-30-migrating-to-subdomain.jpg" alt="Image: Correctly rendered &amp;#39;website under construction&amp;#39; page on schallbert.de" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Automatische Updates für alles!</title><link>https://blog.schallbert.de/server-auto-upgrade/</link><pubDate>Sat, 20 Jan 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/server-auto-upgrade/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-01-20-server-auto-upgrade-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: unattended-upgrades and Watchtower keep my server and application packages up to date."&#10; title="Automatische Updates für alles!" /&gt;&#10;&lt;p&gt;Wie der Titel schon sagt, möchte ich Sicherheitsupdates gern installiert haben, sobald sie erscheinen. Dies gilt gleichsam für das Ubuntu meines Cloud-Servers wie auch für alle Applikationen, die ich in Docker betreibe. Da ich faul bin, möchte ich diese Sicherheitsupdates nicht manuell vornehmen, sondern vollautomatisch ablaufen lassen.&lt;/p&gt;&#10;&lt;p&gt;Für Funktionsupdates und -Erweiterungen hingegen ist es auch weiterhin OK für mich, von Zeit zu Zeit manuell einzugreifen.&#10;Beginnen wir also mit den Systemupgrades.&lt;/p&gt;&#10;&lt;h2 id="sicherheitsupdates-per-unattended-upgrades"&gt;Sicherheitsupdates per &lt;code&gt;unattended-upgrades&lt;/code&gt;&lt;/h2&gt;&#10;&lt;p&gt;Ubuntu stellt - wie eine kurze &lt;a href="https://askubuntu.com/questions/9/how-do-i-enable-automatic-updates" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Suchmaschinenabfrage ergibt&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; - bereits ein konfigurierbares Werkzeug für solche automatisierten Updates zur Verfügung: &lt;code&gt;unattended-upgrades&lt;/code&gt;. Ich prüfe zuerst, ob es bereits auf meinem System vorhanden ist:&lt;/p&gt;&#10;&lt;h3 id="installation"&gt;Installation&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;schallbert:~# which unattended-upgrades&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/bin/unattended-upgrades&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Prima, es ist bereits da. Ansonsten hätte ich die Installation schnell mit &lt;code&gt;apt install unattended-upgrades&lt;/code&gt; nachgeholt.&lt;/p&gt;&#10;&lt;h3 id="unattended-upgrades-konfigurieren"&gt;unattended-upgrades konfigurieren&lt;/h3&gt;&#10;&lt;p&gt;Hier hilft das &lt;a href="https://wiki.debian.org/UnattendedUpgrades" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Debian Wiki&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; weiter: Die Konfigurationsdateien liegen in &lt;code&gt;/etc/apt/apt.conf.d&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Zuerst gehe ich sicher, dass automatische Updates aktiviert sind. Hierfür prüfe ich die Datei &lt;code&gt;20auto-upgrades&lt;/code&gt; und stelle sicher, dass Paketindex-Updates und Upgradeinstallation aktiviert sind:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;APT::Periodic::Update-Package-Lists &lt;span style="color:#e6db74"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;APT::Periodic::Unattended-Upgrade &lt;span style="color:#e6db74"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sehr gut. Jetzt sollte ich noch festlegen, zu welchem Zeitpunkt das Tool loslaufen und welcher Typ Updates installiert werden soll. Hierfür öffne ich die Datei &lt;code&gt;50unattended-upgrades&lt;/code&gt; und entferne die Kommentare bei den entsprechenden Zeilen.&lt;/p&gt;&#10;&lt;h3 id="updatequellen-festlegen"&gt;Updatequellen festlegen&lt;/h3&gt;&#10;&lt;p&gt;Der erste Abschnitt der Konfigurationsdatei widmet sich der erlaubten Quellen für die Updates. Hier sollten selbstverständlich nur vertrauenswürdige Provider stehen. Meine Konfig sieht hier wie folgt aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Unattended-Upgrade::Allowed-Origins &lt;span style="color:#f92672"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;distro_id&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;distro_codename&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;distro_id&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;distro_codename&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;-security&amp;#34;&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;distro_id&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;ESMApps:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;distro_codename&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;-apps-security&amp;#34;&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;distro_id&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;ESM:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;distro_codename&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;-infra-security&amp;#34;&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Erlaubt werden hier Quellen für die von mir verwendete Linux-Distribution im Allgemeinen und Sicherheitsupdates inklusive &lt;code&gt;ESM&lt;/code&gt;, was für &amp;ldquo;Extended Security Maintenance&amp;rdquo; steht.&lt;/p&gt;&#10;&lt;h3 id="updates-auch-für-dev-releases"&gt;Updates auch für Dev Releases?&lt;/h3&gt;&#10;&lt;p&gt;Die folgende Zeile legt fest, ob auch Vorabversionen von Ubuntu Updates erhalten sollen. Bei mir steht der Wert auf &lt;code&gt;auto&lt;/code&gt;.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Unattended-Upgrade::DevRelease &lt;span style="color:#e6db74"&gt;&amp;#34;auto&amp;#34;&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="automatischer-neustart"&gt;Automatischer Neustart&lt;/h3&gt;&#10;&lt;p&gt;Einige Sicherheitsupdates (z.B. den &lt;a href="https://en.wikipedia.org/wiki/Kernel_%28operating_system%29" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Kernel&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; betreffend) erfordern einen Neustart der Maschine. Sie werden daher nur in Kraft treten, wenn man in der Datei entsprechende Werte gesetzt hat. Dabei ist &lt;code&gt;WithUsers&lt;/code&gt; natürlich Geschmackssache, denn man wird ausgeloggt, falls zufälligerweise gerade ein Update installiert wurde, was einen Neustart erfordert. Um das praktisch auszuschließen, habe ich unter &lt;code&gt;Reboot-Time&lt;/code&gt; eine Zeit eingetragen, bei der ich sicher nicht an der Maschine hocke.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Unattended-Upgrade::Automatic-Reboot &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Unattended-Upgrade::Automatic-Reboot-WithUsers &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Unattended-Upgrade::Automatic-Reboot-Time &lt;span style="color:#e6db74"&gt;&amp;#34;03:45&amp;#34;&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unangekündigte Neustarts haben natürlich Rückwirkung auf alle Programme, die ich verwende. Daher stelle ich in meinen &lt;code&gt;docker-compose.yml&lt;/code&gt; Dateien sicher, dass dort bei &lt;code&gt;restart:&lt;/code&gt; auch wirklich &lt;code&gt;always&lt;/code&gt; oder &lt;code&gt;unless-stopped&lt;/code&gt; notiert ist.&lt;/p&gt;&#10;&lt;h3 id="benachrichtigungen"&gt;Benachrichtigungen&lt;/h3&gt;&#10;&lt;p&gt;Automatische Mails oder anderweitige Push-Benachrichtigungen habe ich erst einmal nicht eingeschaltet, da ich mich noch im Testbetrieb befinde und keine große Lust auf einen Haufen weiterer Nachrichten habe. Sobald ich dies ändere, gibt es hier ganz bestimmt ein Update.&lt;/p&gt;&#10;&lt;h3 id="funktionsprüfung"&gt;Funktionsprüfung&lt;/h3&gt;&#10;&lt;p&gt;Um zu sehen, ob unattended-upgrades tut was es soll, schaue ich ein paar Tage später mal in die Logs unter &lt;code&gt;/var/log/unattended-upgrades&lt;/code&gt;.&#10;Hier steht in der Datei &lt;code&gt;unattended-upgrades.log&lt;/code&gt; zum Beispiel folgendes:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;59&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;781&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Starting&lt;/span&gt; unattended upgrades script&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;59&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;781&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Allowed&lt;/span&gt; origins &lt;span style="color:#e6db74"&gt;are&lt;/span&gt;: o&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Ubuntu&lt;/span&gt;,a&lt;span style="color:#f92672"&gt;=&lt;/span&gt;jammy, o&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Ubuntu&lt;/span&gt;,a&lt;span style="color:#f92672"&gt;=&lt;/span&gt;jammy&lt;span style="color:#f92672"&gt;-&lt;/span&gt;security, o&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;UbuntuESMApps&lt;/span&gt;,a&lt;span style="color:#f92672"&gt;=&lt;/span&gt;jammy&lt;span style="color:#f92672"&gt;-&lt;/span&gt;apps&lt;span style="color:#f92672"&gt;-&lt;/span&gt;security, o&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;59&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;781&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Initial&lt;/span&gt; &lt;span style="color:#e6db74"&gt;blacklist&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;59&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;782&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Initial&lt;/span&gt; whitelist (&lt;span style="color:#f92672"&gt;not&lt;/span&gt; strict):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;14&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;33&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;956&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Packages&lt;/span&gt; that will be &lt;span style="color:#e6db74"&gt;upgraded&lt;/span&gt;: libc&lt;span style="color:#f92672"&gt;-&lt;/span&gt;bin libc&lt;span style="color:#f92672"&gt;-&lt;/span&gt;dev&lt;span style="color:#f92672"&gt;-&lt;/span&gt;bin libc&lt;span style="color:#f92672"&gt;-&lt;/span&gt;devtools libc6 libc6&lt;span style="color:#f92672"&gt;-&lt;/span&gt;dev locales python3&lt;span style="color:#f92672"&gt;-&lt;/span&gt;twisted&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;14&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;33&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;956&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Writing&lt;/span&gt; dpkg log to &lt;span style="color:#e6db74"&gt;/var/&lt;/span&gt;log&lt;span style="color:#f92672"&gt;/&lt;/span&gt;unattended&lt;span style="color:#f92672"&gt;-&lt;/span&gt;upgrades&lt;span style="color:#f92672"&gt;/&lt;/span&gt;unattended&lt;span style="color:#f92672"&gt;-&lt;/span&gt;upgrades&lt;span style="color:#f92672"&gt;-&lt;/span&gt;dpkg&lt;span style="color:#f92672"&gt;.&lt;/span&gt;log&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;14&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;54&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;578&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;All&lt;/span&gt; upgrades installed&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Die Datei &lt;code&gt;unattended-upgrades-shutdown.log&lt;/code&gt; ist allerdings noch leer. Ich werde zu einem späteren Zeitpunkt prüfen, ob hier auch alles funktioniert.&lt;/p&gt;&#10;&lt;h2 id="container-updates-mit-watchtower-automatisieren"&gt;Container-Updates mit &lt;code&gt;Watchtower&lt;/code&gt; automatisieren&lt;/h2&gt;&#10;&lt;p&gt;Nun zu den Updates meiner Applikationen. Ein kurzes Gespräch mit ein paar Admins ergab:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Es ist mühsam, alle Programme von Hand auf Stand zu halten&lt;/li&gt;&#10;&lt;li&gt;Für Anwendungen im Container gibt es Dienste, die diese Aufgabe zentral lösen&lt;/li&gt;&#10;&lt;li&gt;Es gab bereits schlechte Erfahrungen, wenn in der &lt;code&gt;docker-compose.yml&lt;/code&gt; auf den aktuellsten Release referenziert wird&lt;/li&gt;&#10;&lt;li&gt;Bei &lt;code&gt;image: &amp;lt;application&amp;gt;:latest&lt;/code&gt; seien hier nicht alle Anwendungen stabil bzw. erzeugten Probleme mit Abhängigkeiten&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Einer Lösung für dieses Problem bietet &lt;a href="https://containrrr.dev/watchtower/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Watchtower&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, welches in meinem Falle selbst wieder in einem Container läuft.&lt;/p&gt;&#10;&lt;h3 id="installation-1"&gt;Installation&lt;/h3&gt;&#10;&lt;p&gt;Wie üblich schreibe ich mir in einem neuen Ordner unter &lt;code&gt;opt/watchtower&lt;/code&gt; eine &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# copied from https://containrrr.dev/watchtower/notifications/&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# watchtower/docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;3&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;watchtower&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;containrrr/watchtower&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;restart&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;unless-stopped&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Watchtower benötigt &lt;code&gt;docker.sock&lt;/code&gt;, um die Updates zu ziehen und auf den vorhandenen Containern anzuwenden.&#10;Ansonsten habe ich hier erst einmal nichts weiter unternommen. Keine Benachrichtigungen (aus obengenannten Gründen), keine Logs, nichts. Ich hoffe, dieses Setup bleibt geräuschlos und ich muss mich hier lange nicht mit einem &lt;em&gt;Update&lt;/em&gt; zu Wort melden.&lt;/p&gt;&#10;&lt;p&gt;Weitergehende Schritte für die Validierung der Funktion von Watchtower finden sich z.B. &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-automatically-update-docker-container-images-with-watchtower-on-ubuntu-22-04" target="_blank" rel="noopener noreferrer" class="external-link"&gt;in einem Tutorial von DigitalOcean&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 id="ablauf"&gt;Ablauf&lt;/h3&gt;&#10;&lt;p&gt;Watchtower prüft die Installierten Docker images &lt;a href="https://containrrr.dev/watchtower/introduction/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;lt. Doku täglich&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; auf Updates. Sollte des ein neues Image-release geben, schickt Watchtower ein &lt;code&gt;SIGTERM&lt;/code&gt;-Signal an upzudatende Container, worauf sich selbige herunterfahren (&lt;a href="https://containrrr.dev/watchtower/stop-signals/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Quelle&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;). Anschließend werden die Container wieder hochgefahren.&lt;/p&gt;&#10;&lt;h3 id="unfreiwillige-funktionsprüfung"&gt;Unfreiwillige Funktionsprüfung&lt;/h3&gt;&#10;&lt;p&gt;Ein paar Tage nach Start des Containers wählte ich mich nochmal in meinen Server ein und sah auf gut Glück ein paar Logs durch. Ich wollte nur kurz nach dem Rechten schauen.&lt;/p&gt;&#10;&lt;p&gt;In den &lt;code&gt;fail2ban&lt;/code&gt; Logdateien sah ich zu einem bestimmten Zeitpunkt, Uhrzeit &lt;code&gt;14:42:56&lt;/code&gt; plötzlich seitenweise Einträge. Mehr davon, als ich die Tage vorher insgesamt gesehen hatte. Als ich zum Beginn dieser Kette von Einträgen hochscrollte, sah ich, dass fail2ban mir wohl kurz ausgestiegen war.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt; 2024-01-26 14:42:50,817 &amp;lt;container_id&amp;gt; INFO Shutdown in progress...&#10; 2024-01-26 14:42:50,817 &amp;lt;container_id&amp;gt; INFO Observer stop ... try to end queue 5 seconds&#10; 2024-01-26 14:42:50,837 &amp;lt;daemon_id&amp;gt; INFO Observer stopped, 0 events remaining.&#10; 2024-01-26 14:42:50,878 &amp;lt;container_id&amp;gt; INFO Stopping all jails&#10; 2024-01-26 14:42:50,879 &amp;lt;container_id&amp;gt; INFO Removed logfile: &amp;#39;/var/log/auth.log&amp;#39;&#10; 2024-01-26 14:42:50,879 &amp;lt;container_id&amp;gt; INFO Removed logfile: &amp;#39;/var/log/caddy2/gitea_access.log&amp;#39;&#10; 2024-01-26 14:42:50,879 &amp;lt;container_id&amp;gt; INFO Removed logfile: &amp;#39;/var/log/caddy2/server_access.log&amp;#39;&#10; 2024-01-26 14:42:51,052 &amp;lt;daemon_id&amp;gt; NOTIC [sshd] Flush ticket(s) with iptables&#10; 2024-01-26 14:42:51,063 &amp;lt;container_id&amp;gt; INFO Jail &amp;#39;sshd&amp;#39; stopped&#10; 2024-01-26 14:42:51,137 &amp;lt;daemon_id&amp;gt; NOTIC [caddy-status] Flush ticket(s) with iptables-multiport&#10; 2024-01-26 14:42:51,137 &amp;lt;container_id&amp;gt; INFO Jail &amp;#39;caddy-status&amp;#39; stopped&#10; 2024-01-26 14:42:51,138 &amp;lt;container_id&amp;gt; INFO Connection to database closed.&#10; 2024-01-26 14:42:51,139 &amp;lt;container_id&amp;gt; INFO Exiting Fail2ban&#10; 2024-01-26 14:42:56,173 &amp;lt;new_container_id&amp;gt; INFO --------------------------------------------------&#10; 2024-01-26 14:42:56,173 &amp;lt;new_container_id&amp;gt; INFO Starting Fail2ban v1.0.2&#10; 2024-01-26 14:42:56,173 &amp;lt;new_container_id&amp;gt; INFO Observer start...&#10;[...]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Die ganzen Log-Einträge zur selben Uhrzeit beginnen am Ende des oben gezeigten Abschnitts. Ein ganzer Haufen IP-Adressen wird dort zur Vorbereitung des Banns in Fail2ban geladen.&#10;Leicht beunruhigt schaute ich ins Syslog von Ubuntu:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;Jan 26 14:42:55 schallbert systemd[1]: docker-&amp;lt;ID&amp;gt;.scope: Deactivated successful&#10;Jan 26 14:42:55 schallbert systemd[1]: docker-&amp;lt;ID&amp;gt;.scope: Consumed 21:05 CPU time&#10;Jan 26 14:42:55 schallbert dockerd[690]: time=&amp;#34;2024-01-26T14:42:55.228607617Z&amp;#34; level=info msg=&amp;#34;ignoring event&amp;#34; &#10;Jan 26 14:42:55 schallbert containerd[639]: time=&amp;#34;2024-01-26T14:42:55.229864632Z&amp;#34; level=info msg=&amp;#34;shim disconnected&amp;#34; &#10;Jan 26 14:42:55 schallbert containerd[639]: time=&amp;#34;2024-01-26T14:42:55.230125409Z&amp;#34; level=warning msg=&amp;#34;cleaning up after shim disconnected&amp;#34; i&amp;gt;&#10;Jan 26 14:42:55 schallbert containerd[639]: time=&amp;#34;2024-01-26T14:42:55.230145478Z&amp;#34; level=info msg=&amp;#34;cleaning up dead shim&amp;#34;&#10;Jan 26 14:42:55 schallbert containerd[639]: time=&amp;#34;2024-01-26T14:42:55.240586557Z&amp;#34; level=warning msg=&amp;#34;cleanup warnings time=\&amp;#34;2024-01-26T14:&amp;gt;&#10;Jan 26 14:42:55 schallbert dockerd[690]: time=&amp;#34;2024-01-26T14:42:55.241839655Z&amp;#34; level=warning msg=&amp;#34;ShouldRestart failed&#10;Jan 26 14:42:55 schallbert containerd[639]: time=&amp;#34;2024-01-26T14:42:55.870912387Z&amp;#34; level=info msg=&amp;#34;loading plugin \&amp;#34;io.containerd.event.v1.p&amp;gt;&#10;Jan 26 14:42:55 schallbert containerd[639]: time=&amp;#34;2024-01-26T14:42:55.870997689Z&amp;#34; level=info msg=&amp;#34;loading plugin \&amp;#34;io.containerd.internal.v&amp;gt;&#10;Jan 26 14:42:55 schallbert containerd[639]: time=&amp;#34;2024-01-26T14:42:55.871007368Z&amp;#34; level=info msg=&amp;#34;loading plugin \&amp;#34;io.containerd.ttrpc.v1.t&amp;gt;&#10;Jan 26 14:42:55 schallbert containerd[639]: time=&amp;#34;2024-01-26T14:42:55.871150831Z&amp;#34; level=info msg=&amp;#34;starting signal loop&amp;#34; namespace=moby path&amp;gt;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hm, &lt;code&gt;cleaning up dead shim&lt;/code&gt;. Das klingt ja bedrohlich. Eine kurze &lt;a href="https://iximiuz.com/en/posts/implementing-container-runtime-shim/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Recherche&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zeigt, dass die Shim eine Zwischenschicht zwischen Containermanager und dem Container selbst bildet und Ein-und Ausgaben des Containers weiterleitet. Ihre Laufzeit ist an die des Containers gekoppelt.&lt;/p&gt;&#10;&lt;p&gt;Also war dies ein normales Herunterfahren mit anschließendem Neustart. Zum Glück kein Einbruch!&#10;Bleibt nur noch der Grund für den Neustart von Fail2ban herauszufinden. Da fällt mir ein, dass ich noch gar nicht weiß, wann Watchtower genau die Updates fährt&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;Also mal schnell in die Logs von Watchtower geschaut mit &lt;code&gt;docker container logs &amp;lt;container_id&amp;gt;&lt;/code&gt; und siehe da:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;time=&amp;#34;2024-01-26T14:42:47Z&amp;#34; level=info msg=&amp;#34;Found new lscr.io/linuxserver/fail2ban:latest image (9bda077d765e)&amp;#34;&#10;time=&amp;#34;2024-01-26T14:42:50Z&amp;#34; level=info msg=&amp;#34;Stopping /fail2ban (e0317a105d53) with SIGTERM&amp;#34;&#10;time=&amp;#34;2024-01-26T14:42:55Z&amp;#34; level=info msg=&amp;#34;Creating /fail2ban&amp;#34;&#10;time=&amp;#34;2024-01-26T14:42:55Z&amp;#34; level=info msg=&amp;#34;Session done&amp;#34; Failed=0 Scanned=6 Updated=1 notify=no&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Damit habe ich den Beweis: Watchtower tut was es soll. Es zieht Container auf eine neue Version hoch, sobald Updates verfügbar werden. Prima!&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;PS:&lt;/strong&gt; Da ich Watchtower nicht groß konfiguriert habe, nehme ich anhand der in den Logs befindlichen Zeitstempel an, dass die Updates täglich zur Zeit des Starts von Watchtower gefahren werden. Bald werde ich von &lt;code&gt;latest&lt;/code&gt; für meine Container wohl auf ein älteres Tag release umschwenken.&lt;/p&gt;&#10;</description></item><item><title>Den Server absichern</title><link>https://blog.schallbert.de/server-protection/</link><pubDate>Fri, 12 Jan 2024 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/server-protection/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2024-01-12-server-protection-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: side-by-side image of fail2ban and borgmatic logos"&#10; title="Den Server absichern" /&gt;&#10;&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;&#10;&lt;p&gt;OK. Nun läuft mein Server, &lt;a href="https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/"&gt;baut automatisch&lt;/a&gt; meinen Blog und &lt;a href="https://blog.schallbert.de/self-hosted-jekyll-page-broken-links/"&gt;zeigt ihn auch problemlos an&lt;/a&gt;. So ganz fertig bin ich aber noch nicht. Denn ich könnte mehr für die Sicherheit des Servers tun, als überall Passwort-Logins zu deaktivieren.&lt;/p&gt;&#10;&lt;p&gt;Außerdem habe ich noch keine Backups. Und das ist ja nie gut. Also, packen wir&amp;rsquo;s an!&lt;/p&gt;&#10;&lt;h2 id="unerwünschte-besucher-aussperren"&gt;Unerwünschte Besucher aussperren&lt;/h2&gt;&#10;&lt;p&gt;Ich bekomme in meinem SSH-Log im Root-Account des Servers einen Haufen Verbindungsversuche, die nicht von mir kommen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;19&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;29&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93842&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;Invalid&lt;/span&gt; user admin from &lt;span style="color:#ae81ff"&gt;41&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;207&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;248&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;204&lt;/span&gt; port &lt;span style="color:#ae81ff"&gt;37194&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;19&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;29&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93842&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: pam_unix(&lt;span style="color:#e6db74"&gt;sshd&lt;/span&gt;:auth): check pass; user unknown&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;19&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;29&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93842&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: pam_unix(&lt;span style="color:#e6db74"&gt;sshd&lt;/span&gt;:auth): authentication failure; logname&lt;span style="color:#f92672"&gt;=&lt;/span&gt; uid&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; euid&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; tty&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ssh ru&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;19&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;31&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93842&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;Failed&lt;/span&gt; password &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; invalid user admin from &lt;span style="color:#ae81ff"&gt;41&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;207&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;248&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;204&lt;/span&gt; port &lt;span style="color:#ae81ff"&gt;37194&lt;/span&gt; ssh2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;19&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;32&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93842&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;Connection&lt;/span&gt; closed by invalid user admin &lt;span style="color:#ae81ff"&gt;41&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;207&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;248&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;204&lt;/span&gt; port &lt;span style="color:#ae81ff"&gt;37194&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;preauth&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93886&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;Invalid&lt;/span&gt; user svn from &lt;span style="color:#ae81ff"&gt;84&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;108&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;40&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;27&lt;/span&gt; port &lt;span style="color:#ae81ff"&gt;44968&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93886&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: pam_unix(&lt;span style="color:#e6db74"&gt;sshd&lt;/span&gt;:auth): check pass; user unknown&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;14&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93886&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: pam_unix(&lt;span style="color:#e6db74"&gt;sshd&lt;/span&gt;:auth): authentication failure; logname&lt;span style="color:#f92672"&gt;=&lt;/span&gt; uid&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; euid&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; tty&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ssh ru&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93886&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;Failed&lt;/span&gt; password &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; invalid user svn from &lt;span style="color:#ae81ff"&gt;84&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;108&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;40&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;27&lt;/span&gt; port &lt;span style="color:#ae81ff"&gt;44968&lt;/span&gt; ssh2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93886&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;Received&lt;/span&gt; disconnect from &lt;span style="color:#ae81ff"&gt;84&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;108&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;40&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;27&lt;/span&gt; port &lt;span style="color:#ae81ff"&gt;44968&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;Bye&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Bye&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;preauth&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Jan&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;00&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; sshd&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;93886&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;Disconnected&lt;/span&gt; from invalid user svn &lt;span style="color:#ae81ff"&gt;84&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;108&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;40&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;27&lt;/span&gt; port &lt;span style="color:#ae81ff"&gt;44968&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;preauth&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Wenn ich nicht wüsste, dass dies das inzwischen übliche &amp;ldquo;Rauschen&amp;rdquo; im Internet ist, würde es mich schon etwas beunruhigen. Ist ja fast so, als würde alle paar Sekunden jemand mit bösen Absichten versuchen, irgendeinen Schlüssel an meiner Haustür auszuprobieren. Was kann man dagegen also tun? Wegschicken!&lt;/p&gt;&#10;&lt;h3 id="fail2ban"&gt;fail2ban&lt;/h3&gt;&#10;&lt;p&gt;Genau dies soll die Software &lt;a href="https://github.com/fail2ban/fail2ban" target="_blank" rel="noopener noreferrer" class="external-link"&gt;fail2ban&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; für mich übernehmen.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt; __ _ _ ___ _ &#10; / _|__ _(_) |_ ) |__ __ _ _ _ &#10; | _/ _` | | |/ /| &amp;#39;_ \/ _` | &amp;#39; \ &#10; |_| \__,_|_|_/___|_.__/\__,_|_||_|&#10; v1.1.0.dev1 20??/??/??&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Vereinfacht gesagt durchforstet &lt;code&gt;fail2ban&lt;/code&gt; Zugriffslogs&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; nach IP-Adressen, für die fehlgeschlagene Anmeldeversuche registriert wurden, und &amp;ldquo;bannt&amp;rdquo; sie bei Überschreiten einer benutzerdefinierten Anzahl Versuche innerhalb einer bestimmten Zeit für einen gewünschten Zeitraum.&lt;/p&gt;&#10;&lt;p&gt;Wie dieser Bannspruch umgesetzt wird? Fail2ban modifiziert die &lt;a href="https://en.wikipedia.org/wiki/Iptables" target="_blank" rel="noopener noreferrer" class="external-link"&gt;iptables&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, greift also auf Paketfilterregeln zu (Stichwort Firewall), welche sich unten auf der Netzwerkschicht befinden. So werden hereinkommende Anfragen bereits geblockter Adressen gar nicht erst bis zu meinen Applikationen durchkommen&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&#10;&lt;h3 id="fail2ban-installieren"&gt;fail2ban installieren&lt;/h3&gt;&#10;&lt;p&gt;&lt;code&gt;fail2ban&lt;/code&gt; scheint so etwas wie Industriestandard bei der Abwehr unerwünschter Zugriffsversuche auf Linux zu sein. Jedem Hobby- und Profiadmin den ich kenne war das Programm geläufig. Ich erntete für meine Unwissenheit des Öfteren ein müdes Lächeln.&lt;/p&gt;&#10;&lt;p&gt;Zu Installation und Konfiguration gibt es bereits einen Haufen Anleitungen da draußen plus der (sehr gut geschriebenen), die im Repo von Fail2ban gleich mitgeliefert wird. Daher gehe ich nicht besonders tief hierauf ein.&lt;/p&gt;&#10;&lt;p&gt;Ich entschied mich für die Installation im Docker-Container, damit ich die üblichen Abhängigkeiten gleich mitgeliefert bekomme. Dafür verwende ich die open-source Distribution von &lt;a href="https://docs.linuxserver.io/images/docker-fail2ban/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;linuxserver&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und verfasse die folgende &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /fail2ban/docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2.1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;fail2ban&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;lscr.io/linuxserver/fail2ban:latest&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;container_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;fail2ban&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cap_add&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;NET_ADMIN&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;NET_RAW&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;network_mode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;host&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;environment&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;PUID=1000&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;PGID=1000&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;TZ=Etc/UTC&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;VERBOSITY=-vv&lt;/span&gt; &lt;span style="color:#75715e"&gt;#optional&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./config:/config&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/var/log/auth.log:/var/log/auth.log:ro&lt;/span&gt; &lt;span style="color:#75715e"&gt;# host ssh&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/var/log/caddy2:/var/log/caddy2:ro &lt;/span&gt; &lt;span style="color:#75715e"&gt;# gitea via caddy, caddyserver&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;restart&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;unless-stopped&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Das Einzige, was hier zu beachten gilt: &lt;code&gt;fail2ban&lt;/code&gt; benötigt die Access logs per Volume zur Verfügung gestellt (oben als &lt;code&gt;:ro&lt;/code&gt; nur mit Lesezugriff angegeben). Eine Menge Filterregeln liegen bereits vorkonfiguriert im &lt;code&gt;config&lt;/code&gt;-Ordner, daher muss ich nur die &lt;code&gt;jail.local&lt;/code&gt; &lt;a href="https://github.com/linuxserver/fail2ban-confs" target="_blank" rel="noopener noreferrer" class="external-link"&gt;nach &lt;code&gt;linuxserver/fail2ban-confs&lt;/code&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; sowie &lt;code&gt;filter.d&lt;/code&gt; für Caddy &lt;a href="https://muetsch.io/how-to-integrate-caddy-with-fail2ban.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;nach muetsch.io&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; anpassen und schon habe ich einen automatischen Türwächter.&lt;/p&gt;&#10;&lt;h3 id="fail2ban-beispiel"&gt;fail2ban Beispiel&lt;/h3&gt;&#10;&lt;p&gt;So sieht ein fail2ban-Log nun für meinen SSH-Daemon aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# schallbert:/opt/fail2ban/config/log/fail2ban# grep &amp;#34;220.124.89.47&amp;#34; fail2ban.log &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;600&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;FBB3630BB38&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;sshd&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Found&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;220&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;124&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;89&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;47&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;23&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;003&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;FBB3630BB38&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;sshd&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Found&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;220&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;124&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;89&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;47&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;22&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;25&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;205&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;FBB3630BB38&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;sshd&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Found&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;220&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;124&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;89&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;47&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;24&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;27&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;206&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;FBB3630BB38&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;sshd&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Found&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;220&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;124&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;89&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;47&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;26&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;610&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;FBB3630BB38&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INFO&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;sshd&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Found&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;220&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;124&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;89&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;47&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2024&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;01&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;11&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;33&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;045&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;FBB36104B38&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NOTIC&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;sshd&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Ban&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;220&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;124&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;89&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;47&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Und tschüss!&lt;/p&gt;&#10;&lt;h3 id="was-noch-nicht-funktioniert-gitea--fail2ban"&gt;Was (noch) nicht funktioniert: Gitea &amp;amp; fail2ban&lt;/h3&gt;&#10;&lt;p&gt;Ich bekomme auch auf meiner Gitea-Instanz ssh-Anfragen rein, die ich ebenfalls gern wegblocken möchte. Allerdings kann ich Gitea bis jetzt partout nicht dazu bekommen, die Logs dafür auch in eine Datei zu schreiben. Bis jetzt werden die stets an die Konsole geschickt, wo ich sie fail2ban natürlich nicht zuführen kann und möchte. Tatsächlich wird die Verbindung zu Gitea per SSH zum Glück bereits gesichert, da sie über denselben &lt;code&gt;sshd&lt;/code&gt; geht wie der Shell-Zugriff auf meinen Server selbst.&lt;/p&gt;&#10;&lt;p&gt;Was mir noch fehlt sind nur die Überwachung fehlgeschlagener Logins auf dem Web-Frontend.&lt;/p&gt;&#10;&lt;p&gt;Dabei sieht Giteas &lt;code&gt;app.ini&lt;/code&gt; für mich sauber aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# gitea/conf/app.ini&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#[...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[log]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;MODE&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;LEVEL&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;warn&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ROOT_PATH&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/data/gitea/log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ENABLE_ACCESS_LOGS&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ENABLE_SSH_LOG&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;logger.access.MODE&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;access-file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[log.access-file]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;MODE&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ACCESS&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;LEVEL&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;info&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;FILE_NAME&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;access.log&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ich habe sowohl die Access-Logs aktiviert &lt;code&gt;ENABLE_ACCESS_LOGS&lt;/code&gt; und den Access-Logger in eine Datei schreiben lassen. Die Logs werden auch erstellt, nur sind da keine Zugriffe drin aufgelistet - diese gehen nach wie vor in die Konsole des Containers. Ich bin mir aber noch unsicher, ob der Reverse-Proxy von Caddy hier vielleicht einen Einfluss hat und er zum Beispiel die Zugriffe vor Gitea bereits abfängt. Aber das finde ich schon irgendwann noch heraus.&lt;/p&gt;&#10;&lt;h3 id="update-aug-2024"&gt;Update Aug-2024&lt;/h3&gt;&#10;&lt;p&gt;Irgendwann fragte ich mich, ob &lt;em&gt;fail2ban&lt;/em&gt; auch tatsächlich auf den iptables meines Servers arbeitet oder nur innerhalb des Containers korrekt funktioniert. Zum Glück war ich &lt;a href="https://stackoverflow.com/questions/59996070/how-to-understand-if-the-fail2ban-ssh-filter-is-working-with-a-new-port" target="_blank" rel="noopener noreferrer" class="external-link"&gt;nicht der Erste mit dieser Frage (stackoverflow)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und fand die vorgeschlagene Lösung ganz charmant:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;im Docker-Container eine beliebige IP sperren (&lt;code&gt;docker exec -it fail2ban sh&lt;/code&gt;): &lt;code&gt;fail2ban-client set sshd banip 111.111.111.111&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;In den iptables nachsehen, ob diese IP dort auftaucht: &lt;code&gt;iptables -n -L --line-numbers&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;In meinem Falle: freuen, denn sie ist da: &lt;code&gt;1 REJECT all -- 111.111.111.111&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Die IP wieder entsperren &lt;code&gt;fail2ban-client set sshd unbanip 111.111.111.111&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="update-jul-2025"&gt;Update Jul-2025&lt;/h3&gt;&#10;&lt;p&gt;Ich habe jetzt eine Lösung für &lt;em&gt;Gitea&lt;/em&gt;, aber auch alle anderen meiner Websites zustande gebracht:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Schutz vor Brute-Force Angriffen per &lt;code&gt;ssh&lt;/code&gt; wie oben beschrieben&lt;/li&gt;&#10;&lt;li&gt;Schutz vor Überlast mittels &lt;a href="https://blog.schallbert.de/fail2ban-with-caddy/"&gt;Rate Limiter&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;Schutz vor Angriffen auf die APIs (&lt;code&gt;404/403&lt;/code&gt; Attacken) mittels &lt;code&gt;caddy-status&lt;/code&gt; Konfiguration für fail2ban, Logs in &lt;code&gt;JSON&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /config/fail2ban/filter.d/caddy-status.conf&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# this regex works for caddy with json-style logs&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;failregex = &amp;#34;client_ip&amp;#34;:&amp;#34;&amp;lt;HOST&amp;gt;&amp;#34;(.*)&amp;#34;status&amp;#34;:(400|401|403|404|500)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;datepattern = \d+&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;ignoreregex =&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="regelmäßige-backups"&gt;Regelmäßige Backups&lt;/h2&gt;&#10;&lt;p&gt;Ein wichtiger Aspekt für mich ist die Möglichkeit, den Server wiederherstellen zu können. Sollten unvorhergesehene Ereignisse eintreten wie ein Update einer Komponente welches die Funktion anderer Programme beeinträchtigt, der Ausfall des Servers, oder sogar der Zugriff fremder Personen auf selbigen - in jedem Falle könnte ich mit einem Backup recht schnell eine neue Instanz des Servers erzeugen, konfigurieren und die Website wieder zum Laufen bringen.&lt;/p&gt;&#10;&lt;p&gt;Diese Updates möchte ich aber ungern selbst von Hand aus meinen Ordnern erzeugen, komprimieren und per &lt;code&gt;SCP / SFTP&lt;/code&gt; herunterladen. Besser soll das vollautomatisch vonstatten gehen, und das im Optimalfall noch kostenfrei. Nach einer kurzen Recherche stellen sich für meine Zwecke die quelloffenen Werkzeuge &lt;a href="https://torsion.org/borgmatic/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;borgmatic&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und &lt;a href="https://restic.net/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;restic&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; als geeignet heraus.&lt;/p&gt;&#10;&lt;p&gt;Ich entscheide mich willkürlich für Borgmatic.&lt;/p&gt;&#10;&lt;h3 id="borgmatic-in-docker-installieren"&gt;Borgmatic in Docker installieren&lt;/h3&gt;&#10;&lt;p&gt;Wie bei allen anderen Komponenten auch, möchte ich Borgmatic in einem Container laufen lassen. Glücklicherweise gibt es da bereits &lt;a href="https://hub.docker.com/r/b3vis/borgmatic/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;eine fertige Lösung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, die ich nur noch ein wenig konfigurieren muss.&lt;/p&gt;&#10;&lt;p&gt;Mein &lt;code&gt;docker-compose&lt;/code&gt; File für Borgmatic sieht wie folgt aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /borgmatic/docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;borgmatic&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ghcr.io/borgmatic-collective/borgmatic&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;container_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;borgmatic&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;${VOLUME_SOURCE}:/mnt/source:ro &lt;/span&gt; &lt;span style="color:#75715e"&gt;# backup source&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;${VOLUME_TARGET}:/mnt/repository &lt;/span&gt; &lt;span style="color:#75715e"&gt;# backup target&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;${VOLUME_ETC_BORGMATIC}:/etc/borgmatic.d/ &lt;/span&gt; &lt;span style="color:#75715e"&gt;# borgmatic config file(s) + crontab.txt&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;${VOLUME_BORG_CONFIG}:/root/.config/borg &lt;/span&gt; &lt;span style="color:#75715e"&gt;# config and keyfiles&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;${VOLUME_SSH}:/root/.ssh &lt;/span&gt; &lt;span style="color:#75715e"&gt;# ssh key for remote repositories&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;${VOLUME_BORG_CACHE}:/root/.cache/borg &lt;/span&gt; &lt;span style="color:#75715e"&gt;# checksums used for deduplication&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# - /var/run/docker.sock:/var/run/docker.sock # add docker sock so borgmatic can start/stop containers to be backupped&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;environment&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;TZ=${TZ}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;BORG_PASSPHRASE=${BORG_PASSPHRASE}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;restart&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;always&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Als Inspiration hierfür habe ich reichlich in der &lt;a href="https://github.com/borgmatic-collective/docker-borgmatic/blob/master/README.md" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Dokumentation auf Github&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; gestöbert.&lt;/p&gt;&#10;&lt;h3 id="borgmatic-konfigurieren"&gt;Borgmatic konfigurieren&lt;/h3&gt;&#10;&lt;p&gt;Alle konkreten Daten (die Angaben in &lt;code&gt;${}&lt;/code&gt;) habe ich zur besseren Übersichtlichkeit in einer &lt;code&gt;.env&lt;/code&gt;-Datei im selben Verzeichnis abgelegt. Ganz nebenbei verhindere ich, aus Versehen Passphrases zu veröffentlichen. Diese Passphrase habe ich mir zusätzlich auf einem Zettel notiert - man weiß ja nie, ob man sie nochmal benötigt.&lt;/p&gt;&#10;&lt;p&gt;Ich muss anschließend nur noch die sich in dem Ordner &lt;code&gt;borgmatic.d/&lt;/code&gt; befindliche &lt;code&gt;config.yml&lt;/code&gt; leicht verändern, indem ich Backup-Quelle, -Ziel und die von mir gewünschten Zeitintervalle für die Sicherungserstellung eintrug und war quasi schon bereit für einen ersten Test.&lt;/p&gt;&#10;&lt;h3 id="borgmatic-funktionstest"&gt;Borgmatic Funktionstest&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec borgmatic bash -c &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;cd &amp;amp;&amp;amp; borgmatic --stats -v 1 --files 2&amp;gt;&amp;amp;1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mit diesem Befehl führe ich über Docker im Container &lt;code&gt;borgmatic&lt;/code&gt; den Befehl zum Anlegen einer Sicherung an, um die Funktion von Borgmatic zu verifizieren.&lt;/p&gt;&#10;&lt;p&gt;Hier kam dann direkt eine Fehlermeldung à la &lt;code&gt;repository does not exist&lt;/code&gt; zurück. Also existiert das Backup-Ziel noch gar nicht. Ein kurzer Blick in die Dokumentation zeigt, dass das Repository erst initialisiert werden muss.&lt;/p&gt;&#10;&lt;p&gt;Dies hole ich mit dem Kommando&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec borgmatic bash -c &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;borgmatic init --encryption repokey-blake2&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;nach, was mit ein &amp;ldquo;leeres&amp;rdquo;, verschlüsseltes Repository anlegt.&#10;Versuche ich erneut, ein Backup zu erzeugen, läuft Borgmatic nun durch und erzeugt mir das Backup im Repository.&lt;/p&gt;&#10;&lt;h3 id="ein-backup-ist-nur-ein-backup"&gt;Ein Backup ist nur ein Backup&amp;hellip;&lt;/h3&gt;&#10;&lt;p&gt;&amp;hellip;wenn man es einspielen kann, sagt mein Kumpel.&lt;/p&gt;&#10;&lt;p&gt;Er hat Recht, aber so richtig traue ich mich nicht, über meine funktionierende Serverkonfiguration drüberzubügeln. Also erstelle ich mir eine &lt;code&gt;docker-compose.restore.yml&lt;/code&gt; Datei nach &lt;a href="https://github.com/borgmatic-collective/docker-borgmatic/blob/master/docker-compose.restore.yml" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Vorbild auf dem Repository&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und ziehe &lt;a href="https://www.modem7.com/books/docker-backup/page/backup-docker-using-borgmatic" target="_blank" rel="noopener noreferrer" class="external-link"&gt;modem7s Anleitung zum Thema&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; hinzu.&lt;/p&gt;&#10;&lt;p&gt;Tatsächlich kann ich durch Ausführen der &lt;code&gt;docker-compose.restore.yml&lt;/code&gt; dann in der Container-Shell folgende Kommandos ausführen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir backuprestoremount&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;borg mount /mnt/repository /backuprestoremount&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir backuprestore&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;borgmatic extract --archive latest --destination /backuprestore&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hier erstelle ich den Ordner &lt;code&gt;backuprestoremount&lt;/code&gt; und lasse ihn von &lt;code&gt;borg&lt;/code&gt; auf mein Backup zeigen. Anschließend extrahiere ich das Backup in den ebenfalls neu erstellten Ordner &lt;code&gt;backuprestore&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Nun prüfe ich, ob auch &amp;ldquo;alles da ist&amp;rdquo;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# cd /mnt/backuprestore&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /restore/mnt/source ls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;borgmatic caddy2 containerd fail2ban gitea hostedtoolcache watchtower&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yeah, alle Applikationen sind vorhanden und die enthaltenen Daten sind gesichert! Weiter gehe ich jetzt mal nicht, denn ich bin zu feige (und zu faul), die auf dem Server unter &lt;code&gt;/opt&lt;/code&gt; befindlichen Container zu überschreiben.&lt;/p&gt;&#10;&lt;h3 id="was-noch-nicht-funktioniert-borgmatic--docker-compose-down"&gt;Was (noch) nicht funktioniert: Borgmatic &amp;amp; docker-compose down&lt;/h3&gt;&#10;&lt;p&gt;Borgmatic bietet eine einfache Möglichkeit in der Konfigurationsdatei, Aktionen vor- und nach dem Backup auszuführen.&lt;/p&gt;&#10;&lt;p&gt;Da ich die Konsistenz der Datenbanken meiner zu sichernden Container sicherstellen möchte, sollten die Container zum Zeitpunkt des Backups heruntergefahren sein. Daher schrieb ich mir ein Skript, was alle Applikationen vor dem Backup per &lt;code&gt;docker compose down&lt;/code&gt; stoppt und nach dem Backup wieder hochfährt.&lt;/p&gt;&#10;&lt;p&gt;Dies würde auch alles prima funktionieren, wenn Borgmatic nicht selbst in einem Container liefe. Durch die Kapselung bin ich nun nicht in der Lage, die im Stamm-Dateisystem der Maschine befindlichen Skripte auszuführen. Kurzzeitig dachte ich, dass ich dies mittels Durchreichung von &lt;code&gt;var/run/docker.sock&lt;/code&gt; in den Borgmatic-Container umgehen könne, scheiterte dann aber an der Ankopplung an Docker compose.&lt;/p&gt;&#10;&lt;p&gt;Ich bin mir sicher, dass das zu lösen ist. Allerdings habe ich gerade dringendere Themen. Daher begnüge ich mich mit der Annahme, dass schon nichts passiert, wenn ich das Backup nachts ziehe&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&#10;&lt;p&gt;Auch bei den Auto-Backups scheint es noch zu haken:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;/ # borgmatic --stats&#10;Starting a backup job.&#10;Failed to create/acquire the lock /mnt/repository/lock (timeout).&#10;local: Error running actions for repository&#10;Command &amp;#39;borg create --stats /mnt/repository::{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f} /etc/borgmatic.d/config.yml /mnt/source /root/.borgmatic&amp;#39; returned non-zero exit status 2.&#10;Error while creating a backup.&#10;/etc/borgmatic.d/config.yml: An error occurred&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dies entnehme ich der über Docker aufgerufenen Log-Konsole von Borgmatic. Ich muss also beizeiten nochmal um meine Backups kümmern.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Beim SSH-Daemon meines Servers liegen die Zugriffslogs z.B. in &lt;code&gt;/var/log/auth.log&lt;/code&gt;, können aber auch als &lt;code&gt;access.log&lt;/code&gt; oder ähnlich abgelegt werden. Auch von mir verwendete Dienste wie Gitea und Caddy legen solche Logdateien an.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:2"&gt;&#10;&lt;p&gt;Was ich allerdings noch nicht verstehe ist, wie &lt;code&gt;fail2ban&lt;/code&gt; im Container laufend überhaupt an die iptables herankommt. Ich dachte, dass ein positiver Nebeneffekt der Containerisierung in der Kapselung liegt. Oder ist sie nur bei &amp;ldquo;rootless&amp;rdquo;-Containern zu erreichen?&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:3"&gt;&#10;&lt;p&gt;Hintergrund: Ich bin zur Zeit der Einzige, der Inhalte auf &lt;code&gt;gitea&lt;/code&gt; hochlädt. Meine Webseite ist statisch. Auf dem Webserver &lt;code&gt;caddy&lt;/code&gt; ändern sich also nur dann Dateien, wenn ich sie über Gitea hochlade. Programmupdates über &lt;code&gt;watchtower&lt;/code&gt; und &lt;code&gt;unattended-upgrades&lt;/code&gt; &lt;a href="https://blog.schallbert.de/server-auto-upgrade/"&gt;(Thema eines weiteren Artikels)&lt;/a&gt; habe ich so gelegt, dass sie nicht während des Durchlaufs von &lt;code&gt;borgmatic&lt;/code&gt; stattfindet. Einzig die Logs, welche bei &lt;code&gt;fail2ban&lt;/code&gt; aufschlagen, werden auch zur Zeit der Erstellung des Backups geschrieben. Das Risiko des Datenverlustes gehe ich an der Stelle bewusst aber ein, da die Paketfilterung für geblockte IP-Adressen durch fail2ban nach einer von mir bestimmten Zeit sowieso wieder aufgehoben wird.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Jekyll build: defekte relative Links</title><link>https://blog.schallbert.de/self-hosted-jekyll-page-broken-links/</link><pubDate>Fri, 29 Dec 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/self-hosted-jekyll-page-broken-links/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-12-29_serves_broken_links-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Webpage, displaying Schallbert&amp;#39;&amp;#39;s Blog with broken links"&#10; title="Jekyll build: defekte relative Links" /&gt;&#10;&lt;h2 id="das-problem"&gt;Das Problem&lt;/h2&gt;&#10;&lt;p&gt;Jetzt bin ich nach &lt;a href="https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/"&gt;ewigem Probieren&lt;/a&gt; nun endlich in der Lage, meinem Caddyserver die gebauten Dateien per Docker-Volume zur Verfügung zu stellen. Leider sieht die Seite aber wie folgt aus:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-12-29_serves_broken_links.jpg" alt="Image: Broken webpage, displaying Schallbert&amp;#39;s Blog without pictures, and other media, and any links lead to 404-nowhere" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="die-analyse"&gt;Die Analyse&lt;/h2&gt;&#10;&lt;p&gt;Zuerst hatte ich den Webserver in Verdacht.&#10;Wenn ich die Website aber testweise in meinem Browser aufrufe&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;, zeigt sich dasselbe Verhalten. Merkwürdig.&lt;/p&gt;&#10;&lt;p&gt;Offensichtlich fehlen sämtliche Stylesheets. Sieht man genau hin, sind aber auch Bilder und andere Medien nicht geladen worden, sondern lediglich ihre Beschreibung.&#10;Dann klicke ich wild auf ein paar Links zu Artikeln und Unterseiten. Dabei fällt auf, dass die Pfade anders aussehen, als ich erwartet hätte:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# --- Erwartung ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Bilddatei&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;/assets/images/test.jpg&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Artikel&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;/this-is-a-post&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Stattdessen sehe ich:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# --- Beobachtung ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Bilddatei&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;/pages/&amp;lt;username&amp;gt;/&amp;lt;repositoryname&amp;gt;/assets/images/test.jpg&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Artikel&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;/pages/&amp;lt;username&amp;gt;/&amp;lt;repositoryname&amp;gt;/this-is-a-post&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dies zeigen auch die mit &lt;code&gt;F12&lt;/code&gt; zugeschalteten Entwickleroptionen des Browsers (siehe Bild oben): sämtliche per relativen Link eingebundene Quellen können nicht geladen werden. Übrigens ist es im Allgemeinen eine klare Empfehlung, die Entwickleroptionen bei Problemen mit der Darstellung von Webseiten aktiviert zu haben.&lt;/p&gt;&#10;&lt;p&gt;Liegt ja irgendwie nahe, dass meine Jekyll-Konfigurationsdatei &lt;code&gt;_config.yml&lt;/code&gt; oder meine &lt;a href="https://blog.schallbert.de/jekyll-polyglot-language-support/"&gt;Übersetzungsheader&lt;/a&gt; &lt;code&gt;i10.yml&lt;/code&gt; Fehler enthalten. Dafür gibt es zum Glück bereits hervorragende &lt;a href="https://mademistakes.com/mastering-jekyll/site-url-baseurl/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Hilfeseiten&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Ein Test zeigt jedoch schnell, dass ich die Angabe &lt;code&gt;repository&lt;/code&gt;, woher der Build &lt;code&gt;&amp;lt;username&amp;gt;/&amp;lt;repositoryname&amp;gt;&lt;/code&gt; herbezieht, nicht einfach leer lassen kann:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bundle exec jekyll serve&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Generating... &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Jekyll Feed: Generating feed &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; posts&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Liquid Exception: No repo name found. &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Specify using PAGES_REPO_NWO environment variables, &lt;span style="color:#e6db74"&gt;&amp;#39;repository&amp;#39;&lt;/span&gt; in your configuration, &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; or set up an &lt;span style="color:#e6db74"&gt;&amp;#39;origin&amp;#39;&lt;/span&gt; git remote pointing to your github.com repository. in /_layouts/default.html&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ERROR: YOUR SITE COULD NOT BE BUILT&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Und, verflixt, wo kommt der Unterordner &lt;code&gt;/pages&lt;/code&gt; plötzlich her?&lt;/p&gt;&#10;&lt;h2 id="die-lösung"&gt;Die Lösung&lt;/h2&gt;&#10;&lt;p&gt;Durch das &lt;code&gt;/pages&lt;/code&gt; im Pfad liegt die Vermutung nahe, dass die Ursache in einer Kombination aus &lt;a href="https://jekyllrb.com/docs/configuration/options/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Jekyll-Bauoptionen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und dem &lt;a href="https://github.com/github/pages-gem" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Github-Pages Gem&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zu finden ist.&lt;/p&gt;&#10;&lt;p&gt;Natürlich bin ich mit diesem Problem nicht allein, und nach längerer Recherche finde ich &lt;a href="https://stackoverflow.com/questions/51869314/jekyll-serve-generate-wrong-path-in-localhost" target="_blank" rel="noopener noreferrer" class="external-link"&gt;tatsächlich eine Dokumentation der Lösung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Also ändere ich die Umgebungsvariable &lt;code&gt;JEKYLL_ENV&lt;/code&gt; (steht für die Bauumgebung) von &lt;code&gt;production&lt;/code&gt; auf &lt;code&gt;development&lt;/code&gt;, so wie es normalerweise für das lokale Bauen vorgesehen ist.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# workflows/jekyll-build-action.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;env&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;JEKYLL_ENV&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;development&lt;/span&gt; &lt;span style="color:#75715e"&gt;# I had &amp;#34;production&amp;#34; here before&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Und, was soll ich sagen: Kaum macht man es richtig, schon funktioniert&amp;rsquo;s!&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-12-29_css_assets_show_correctly.jpg" alt="Image: blog.schallbert.de shows correct CSS and media are loaded along working links" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Die Website offline darstellen funktioniert wie folgt: Die gebauten Dateien (den &lt;code&gt;_site&lt;/code&gt;-Ordner) auf den eigenen Rechner ziehen und die &lt;code&gt;index.html&lt;/code&gt; mit dem Browser öffnen.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>🗸 Gitea Actions Teil2 - Jekyll-Dockerimage</title><link>https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/</link><pubDate>Sat, 09 Dec 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-12-09_gitea_infrastructure-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: giteas infrastructure in a nutshell"&#10; title="🗸 Gitea Actions Teil2 - Jekyll-Dockerimage" /&gt;&#10;&lt;h2 id="die-vorgeschichte"&gt;Die Vorgeschichte&lt;/h2&gt;&#10;&lt;p&gt;Leider hatte ich im &lt;a href="https://blog.schallbert.de/gitea-action-runner-native-jekyll/"&gt;Teil1&lt;/a&gt; mit einer leicht modifizierten Kopie der Github-Action zum Bauen von Jekyll kein Glück. Daher wollte ich nun probieren, Jekyll direkt als Dockerimage laufen zu lassen. Im Docker hub gab es nicht so furchtbar viel Auswahl. Trotzdem habe ich ein (in 2023) aktiv gewartetes und gut dokumentiertes Image finden können.&lt;/p&gt;&#10;&lt;h2 id="set-up-job"&gt;&amp;ldquo;Set up job&amp;rdquo;&lt;/h2&gt;&#10;&lt;p&gt;Und so binde ich das Docker-Jekyllimage dann in meinen workflow ein:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# workflows/jekyll-build-action.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Build job&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;build&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runs-on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ubuntu-latest&lt;/span&gt; &lt;span style="color:#75715e"&gt;# this is the &amp;#34;label&amp;#34; the runner will use and map to docker target OS&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;container&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;jvconseil/jekyll-docker&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hier wird dem &lt;code&gt;gitea act_runner&lt;/code&gt; gesagt, dass er mit dem &lt;code&gt;ubuntu-latest&lt;/code&gt; label laufen soll, was in meinem Fall auf eine sehr kleine und schnell hochzufahrende &lt;code&gt;node16:bullseye&lt;/code&gt; Maschine (abgeleitet vom Betriebssystem Debian11) gemappt wird. Zusätzlich wird Docker mitgeteilt, dass bitte das &lt;code&gt;jekyll-docker&lt;/code&gt; image zu laden ist, wo die von mir benötigten Abhängigkeiten (Ruby, Sass, Jekyll&amp;hellip;) bereits berücksichtigt wurden.&lt;/p&gt;&#10;&lt;h2 id="bundle-install"&gt;bundle install&lt;/h2&gt;&#10;&lt;p&gt;Das Bauen schlägt hier allerdings fehl wegen in der &lt;code&gt;Gemfile&lt;/code&gt; angegebenen, aber nicht installierten Abhängigkeiten. Das kann dann so ählich aussehen wie hier:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;bundler: failed to load command: jekyll (/usr/gem/bin/jekyll)&#10;/usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.22/lib/bundler/resolver.rb:332:in `raise_not_found!&amp;#39;: Could not find gem &amp;#39;github-pages&amp;#39; in locally installed gems. (Bundler::GemNotFound)&#10;&#9;from /usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.22/lib/bundler/resolver.rb:392:in `block in prepare_dependencies&amp;#39;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href="https://bundler.io" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Bundler&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist der Paketmanager für in Ruby geschriebene Software. Ähnlich wie &lt;code&gt;npm&lt;/code&gt; für Javascript oder &lt;code&gt;pip&lt;/code&gt; für Python ist &lt;code&gt;bundler&lt;/code&gt; in der Lage, von einem Ruby-Programm genutzte Bibliotheken und andere Abhängigkeiten herunterzuladen und so einzubinden, dass sie ohne weitere Konfiguration auf dem Zielsystem verwendet werden können. Damit &lt;code&gt;bundler&lt;/code&gt; weiß, was zu installieren ist, wird eine sogenannte &lt;code&gt;Gemfile&lt;/code&gt; angelegt.&lt;/p&gt;&#10;&lt;p&gt;Also dem Script schnell &lt;code&gt;bundle install&lt;/code&gt; hinzufügen, etwa so:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# workflows/jekyll-build-action.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;CHECKOUT ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;INSTALL GEMS ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bundle install&lt;/span&gt; &lt;span style="color:#75715e"&gt;# will fail with permissions rights to write to Gemfile.lock but anyways installs required dependencies.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;BUILD WITH JEKYLL ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Outputs to the &amp;#39;./_site&amp;#39; directory by default&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bundle exec jekyll build --destination /opt/blog_staging&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;env&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;JEKYLL_ENV&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;production&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="act_runner-kein-schreibzugriff-auf-gemfilelock"&gt;act_runner: Kein Schreibzugriff auf &lt;code&gt;Gemfile.lock&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;Dies schlägt wieder fehl - wegen fehlender Schreibrechte des &lt;code&gt;act_runner&lt;/code&gt;:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;gitea-runner-1 | [Deploy Jekyll site/build] | `/workspace/schallbert/blog/Gemfile.lock`. It is likely that you need to grant&#10;gitea-runner-1 | [Deploy Jekyll site/build] | write permissions for that path.&#10;gitea-runner-1 | [Deploy Jekyll site/build] ❌ Failure - Main ---INSTALL GEMS ---&#10;gitea-runner-1 | [Deploy Jekyll site/build] exitcode &amp;#39;23&amp;#39;: failure&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nach langer Suche muss ich feststellen, dass &lt;code&gt;Gemfile.lock&lt;/code&gt; auf meinem Rechner nicht der Versionskontrolle unterliegt, da es in der &lt;code&gt;.gitignore&lt;/code&gt; auftaucht. Der Bundler des &lt;code&gt;act_runner&lt;/code&gt; versucht nun also, diese Datei selbst aus der &lt;code&gt;Gemfile&lt;/code&gt; zu erzeugen und scheitert, weil keine Schreibrechte auf dem Repository bzw. der temporären Kopie vorliegen.&lt;/p&gt;&#10;&lt;p&gt;Dies lässt sich am besten beheben, indem die &lt;code&gt;Gemfile.lock&lt;/code&gt; in die Versionskontrolle aufgenommen wird, d.h. aus der &lt;code&gt;.gitignore&lt;/code&gt; verschwindet. Somit stelle ich sicher, dass ich für das lokale Bauen auf meinem Laptop dieselben Umgebungsbedingungen habe wie auf dem Server.&lt;/p&gt;&#10;&lt;h2 id="update-nov-2025"&gt;Update Nov-2025&lt;/h2&gt;&#10;&lt;p&gt;Nach einem Update meiner lokalen Bildumgebung per &lt;code&gt;bundle update&lt;/code&gt; stellt sich bei mir nun erneut das oben genannte Problem, obwohl ich &lt;code&gt;Gemfile.lock&lt;/code&gt; inzwischen versioniere. Die Lösung diskutiere ich in einem &lt;a href="https://blog.schallbert.de/bundler-ci-gemfile-issue/"&gt;Artikel zum Thema Bundler&lt;/a&gt;&lt;/p&gt;&#10;&lt;h3 id="abhängigkeiten-sass-embedded"&gt;Abhängigkeiten: &lt;code&gt;sass-embedded&lt;/code&gt;&lt;/h3&gt;&#10;&lt;p&gt;Nach weniger als 20 Sekunden die nächste Fehlermeldung:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;Resolving dependencies...&#10;Could not find gem &amp;#39;sass-embedded (= 1.69.5)&amp;#39; with platform &amp;#39;x86_64-linux&amp;#39; in&#10;rubygems repository https://rubygems.org/ or installed locally.&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nach weiteren Minuten der Recherche die &lt;a href="https://github.com/helaili/jekyll-action/issues/150#issuecomment-1374388728" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Lösung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;: In der Gemfile eine bestimmte Version des &lt;code&gt;jekyll-sass-converter&lt;/code&gt; spezifizieren oder alternativ das Gem &amp;ldquo;github-pages&amp;rdquo; in der Gemfile belassen. Dieses kümmert sich ebenfalls darum, dass der CascadingStyleSheets (CSS) Präprozessor &lt;a href="https://sass-lang.com/guide/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Sass&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; vorhanden ist.&lt;/p&gt;&#10;&lt;p&gt;Nun endlich läuft &lt;code&gt;bundle install&lt;/code&gt; erfolgreich durch:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;✅ Bundle complete! 5 Gemfile dependencies, 43 gems now installed.&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="jekyll-build"&gt;jekyll build&lt;/h2&gt;&#10;&lt;p&gt;Damit Jekyll die gebauten Dateien auch irgendwo abspeichern kann, habe ich dem &lt;code&gt;act_runner&lt;/code&gt; in der &lt;code&gt;docker-compose.yml&lt;/code&gt; ein weiteres &lt;a href="https://docs.docker.com/storage/volumes/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Volume&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; gegönnt:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# gitea/docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./runner/blog_staging:/opt/blog_staging&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./runner/data:/data&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Und jetzt direkt nochmal probieren, ob Jekyll erfolgreich baut:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt; Destination: /opt/blog_staging&#10; Generating... &#10; Jekyll Feed: Generating feed for posts&#10;jekyll 3.9.3 | Error: Permission denied @ dir_s_mkdir - /opt/blog_staging&#10;/usr/local/lib/ruby/3.2.0/fileutils.rb:406:in `mkdir&amp;#39;: Permission denied @ dir_s_mkdir - /opt/blog_staging (Errno::EACCES)&#10;&#9;from /usr/local/lib/ruby/3.2.0/fileutils.rb:406:in `fu_mkdir&amp;#39;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Auch dieses Problem lässt sich leicht lösen, indem man entweder nach &lt;code&gt;/tmp&lt;/code&gt; baut, wo der user &lt;code&gt;jekyll&lt;/code&gt; während des Bauens ebenfalls Zugriff hat, oder indem man der Action vor dem &lt;code&gt;jekyll build&lt;/code&gt;-Befehl einen Nutzerwechsel im Build-Arbeitsverzeichnis vornimmt: &lt;code&gt;chown -R jekyll /opt/blog_staging&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Mit dieser Änderung läuft die Action endlich durch.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-12-09_buildsuccessful.jpg" alt="Image: Gitea snapshot showing a successful build" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;h2 id="artefakte-transferieren"&gt;Artefakte transferieren&lt;/h2&gt;&#10;&lt;p&gt;Doch wo finde ich jetzt die in der Action gebauten Dateien, welche mein Webserver dann veröffentlichen soll? Der &lt;code&gt;_site&lt;/code&gt;-Ordner ist nirgendwo zu finden - weder auf dem Volume der Hostmaschine, noch im Gitea-Dockercontainer oder im Runner!&lt;/p&gt;&#10;&lt;h3 id="docker-volumes-für-act_runner-können-artefakte-nicht-verteilen"&gt;Docker volumes für &lt;code&gt;act_runner&lt;/code&gt; können Artefakte nicht verteilen&lt;/h3&gt;&#10;&lt;p&gt;Dass ich über Docker volumes nicht dort herankomme erscheint logisch wenn man weiß, wie &lt;code&gt;act-runner&lt;/code&gt; funktioniert: Die Action erzeugt ein eigenes Dockerimage mit wiederum per Task-ID assoziierten Volumes, welches nur für dessen Laufzeit existiert.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;DRIVER VOLUME NAME&#10;--&amp;gt; local GITEA-ACTIONS-TASK-84_WORKFLOW-Deploy-Jekyll-site_JOB-build &amp;lt;--&#10;--&amp;gt; local GITEA-ACTIONS-TASK-84_WORKFLOW-Deploy-Jekyll-site_JOB-build-env &amp;lt;--&#10;local act-toolcache&#10;local blog_staging&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Legt man die Artefakte also nirgendwo extern ab, so werden sie nach Durchlauf der Aktionen zusammen mit dem Image demontiert und sind futsch. Das &lt;a href="https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/#jekyll-build"&gt;oben&lt;/a&gt; angelegte Volume des Runners hat gar keine Auswirkung auf die Action, weil sie wieder in einem isolierten Image läuft.&lt;/p&gt;&#10;&lt;p&gt;Dies zu verstehen hat mich einige Zeit und Fehlversuche gekostet, dabei ist es &lt;a href="https://docs.gitea.com/usage/actions/act-runner?_highlight=runner#register-the-runner-with-docker" target="_blank" rel="noopener noreferrer" class="external-link"&gt;in einem Abschnitt der Dokumentation&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; beschrieben.&lt;/p&gt;&#10;&lt;p&gt;Das Ganze gestaltet sich ja deutlich komplizierter als ich dachte. Mit Kanonen auf Spatzen schießen angesichts meiner kleinen Website und als Einziger, der sie pflegt. Egal, ich ziehe das jetzt durch.&lt;/p&gt;&#10;&lt;h3 id="aber-man-kann-doch-upload-artifact-verwenden"&gt;Aber man kann doch &lt;code&gt;upload-artifact&lt;/code&gt; verwenden!&lt;/h3&gt;&#10;&lt;p&gt;Na ja, das dachte ich jedenfalls. Also habe ich mein Action-Skript entsprechend erweitert und die von Github Actions zur Verfügung gestellte Funktion eingebaut:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# workflows/jekyll-build-action.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Automatically upload the build folder to Giteas blog repo folder&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;UPLOAD ARTIFACT ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/upload-artifact@v3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;path&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/workspace/schallbert/blog/&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Blog_Staging&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;retention-days&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Super, jetzt erscheint ein herunterladbares Zip-file auf der Weboberfläche von Gitea:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-12-09_uploadsuccessful.jpg" alt="Image: Gitea snapshot showing a successful artifact upload" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Nur: Wie bekomme ich das Ding jetzt automatisiert auf meine Serverinstanz? Schließlich kennt die Action diese nicht - sie hat ja nicht einmal einen eigenen Docker Daemon.&lt;/p&gt;&#10;&lt;p&gt;Also schaue ich mal im Gitea-Container selbst nach, wo die Artefakte unter &lt;code&gt;/data/gitea/actions_artifacts/BUILD_ID&lt;/code&gt; abgelegt sind. Allerdings als ein Haufen (hunderte) &lt;code&gt;.chunk.gz&lt;/code&gt; Dateien mit kryptischen Nummernfolgen als Name, bei denen ich wiederum nicht weiß wie ich sie zu einem Archiv zusammenfügen soll.&lt;/p&gt;&#10;&lt;p&gt;Und noch etwas gefällt mir an &lt;code&gt;upload-artifact&lt;/code&gt; nicht: Da meine Website inzwischen eine Menge Bilder, Audiodateien und Videos enthält, ist sie recht groß. Daher benötigt der uploader für&amp;rsquo;s Verpacken und Versenden schon jetzt &lt;em&gt;fast zwei Minuten&lt;/em&gt; - Tendenz linear steigend mit der Anzahl meiner Medieninhalte.&lt;/p&gt;&#10;&lt;p&gt;Sackgasse.&lt;/p&gt;&#10;&lt;h3 id="auch-docker-cp-funktioniert-in-diesem-falle-nicht"&gt;Auch Docker &lt;code&gt;cp&lt;/code&gt; funktioniert in diesem Falle nicht&lt;/h3&gt;&#10;&lt;p&gt;Dann versuche ich mit Hilfe des Kopierbefehls von Docker, die Artefakte auf meinen Host zu transferieren:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# workflows/jekyll-build-action.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;COPY ARTIFACT ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;docker cp gitea-runner-1:/workspace/schallbert/blog /tmp/blog_staging&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dies schlägt wieder fehl:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | docker cp gitea-runner-1:/workspace/schallbert/blog /tmp/blog_staging&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;...&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Deploy Jekyll site/build&lt;span style="color:#f92672"&gt;]&lt;/span&gt; | /var/run/act/workflow/3.sh: line 2: docker: not found&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Deploy Jekyll site/build&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ❌ Failure - Main --- COPY ARTIFACT ---&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Auch erwartbar, denn der Action-Container selbst kennt kein Docker. Es ist also nicht möglich, etwas von &amp;ldquo;innerhalb&amp;rdquo; des Containers per Docker heraus zu bekommen. Dies ist für mich aber erforderlich, da die Action ja den ganzen Prozess vollständig automatisieren soll.&lt;/p&gt;&#10;&lt;p&gt;Oder geht es doch irgendwie? Der Daemon der Hostmaschine &lt;code&gt;docker.sock&lt;/code&gt; steht laut Giteas Dokumentation zumindest dem &lt;code&gt;act_runner&lt;/code&gt; per Volume zur Verfügung - wird aber scheinbar nicht in die Action weitergeleitet. Vielleicht aus gutem Grund, sonst wäre die Kapselung futsch und eine schadhafte Action könnte praktisch meinen Docker Daemon auf dem Host kapern 😰.&lt;/p&gt;&#10;&lt;h3 id="transferieren-per-sftp-würde-klappen"&gt;Transferieren per SFTP würde klappen&lt;/h3&gt;&#10;&lt;p&gt;So langsam gehen mir die Alternativen aus. Ich überlege nun, die Artefakte per &lt;a href="https://de.wikipedia.org/wiki/SSH_File_Transfer_Protocol" target="_blank" rel="noopener noreferrer" class="external-link"&gt;SFTP&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; aus dem vom &lt;code&gt;act_runner&lt;/code&gt; erzeugten Action-Dockerimage heraus in den Webserver-Container zu schieben&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-12-09_sftp_infrastructure.jpg" alt="Image: Machine architecture if I used SFTP for artifact share between Gitea and webserver" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Glücklicherweise gibt es hierfür wieder eine vorgefertigte Action: &lt;a href="https://github.com/marketplace/actions/scp-files" target="_blank" rel="noopener noreferrer" class="external-link"&gt;scp-files&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Nun muss ich aber den Container des Webservers so umbauen, dass er per SCP erreichbar ist und den SSH-Schlüssel der Action akzeptiert.&lt;/p&gt;&#10;&lt;p&gt;Andererseits wird davor &lt;a href="https://stackoverflow.com/questions/65381311/run-sshd-in-docker-container" target="_blank" rel="noopener noreferrer" class="external-link"&gt;in vielen Foren&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; gewarnt - Ein Container sollte nur eine Applikation beherbergen. Aber auch hier gibt es eine Lösung: &lt;a href="https://hub.docker.com/r/panubo/sshd/#!" target="_blank" rel="noopener noreferrer" class="external-link"&gt;ein eigener Docker-Container mit SSH-Daemon&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Diesem könnte ich dann ein Volume geben, welches er mit dem Webserver teilt.&lt;/p&gt;&#10;&lt;p&gt;Sieht leider nach einigem Aufwand aus. Und ich möchte als fauler Entwickler mein System so einfach wie möglich halten - also recherchiere ich weiter.&lt;/p&gt;&#10;&lt;h3 id="action-volumes"&gt;Dem Action-Container ein Volume zur Verfügung stellen&lt;/h3&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-12-09_gitea_infrastructure.jpg" alt="Image: Docker-Gitea infrastructure overview" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Diese ganzen Fehlschläge und Sackgassen begleiten mich jetzt schon seit über einem Monat. Ich will es trotzdem unbedingt schaffen, vollautomatisch bauen, testen und veröffentlichen zu können. Auf dem Weg hierhin habe ich eine Menge gelernt und jetzt hoffe ich, dass dieser Ansatz mich endlich zur Lösung bringt.&lt;/p&gt;&#10;&lt;p&gt;Bei &lt;a href="https://gitea.com/gitea/act_runner/issues/329" target="_blank" rel="noopener noreferrer" class="external-link"&gt;meiner Suche&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; stieß ich auf &lt;a href="https://gitea.com/gitea/act_runner/src/branch/main/internal/pkg/config/config.example.yaml" target="_blank" rel="noopener noreferrer" class="external-link"&gt;die Möglichkeit, die Konfiguration des Runners&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zu verändern. So könnte ich am Ende vielleicht doch noch ein Volume einbinden, welches ich zwischen der Action und meinem Server über den Daemon auf dem Host teilen kann.&lt;/p&gt;&#10;&lt;p&gt;Wenn das klappt, hätte ich kein zusätzliches Sicherheitsrisiko wie ein öffentlich verfügbarer &lt;code&gt;file transfer container&lt;/code&gt; oder die langen Wartezeiten durch &lt;code&gt;upload-artifact&lt;/code&gt;. Also frisch ans Werk! Es gibt noch &lt;a href="https://gitea.com/gitea/act_runner/issues/407" target="_blank" rel="noopener noreferrer" class="external-link"&gt;weitere Gründe&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, weswegen Volumes in der jeweiligen Action Sinn machen können - es muss also bereits Leute da draußen geben, die das hinbekommen haben.&lt;/p&gt;&#10;&lt;p&gt;Ich versuche also, die Volumes im Action-Skript anzulegen:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# workflows/jekyll-build-action.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;container&lt;/span&gt;: &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;jvconseil/jekyll-docker:latest&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;: &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/tmp/blog_staging:/blog_staging&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/opt/cache:/opt/hostedtoolcache&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Doch irgendwie landen die Artefakte noch immer nicht auf der Platte meines Hosts. Ich vereinfache die Action soweit, dass ich nur noch prüfe, ob die Erzeugung des Volumes auch einen entsprechenden Ordner anlegt:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# workflows/jekyll-build-action.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: --- &lt;span style="color:#ae81ff"&gt;CHECK_VOLUME ---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; ls -al /blog_staging&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;und schon schlägt die Aktion wieder fehl:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;gitea-runner-1 | [Deploy Jekyll site/build] [DEBUG] Working directory &amp;#39;/workspace/schallbert/blog&amp;#39;&#10;gitea-runner-1 | [Deploy Jekyll site/build] | ls: /blog_staging: No such file or directory&#10;gitea-runner-1 | [Deploy Jekyll site/build] ❌ Failure - Main --- CHECK_VOLUME ---&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nach ein paar weiteren, erfolglosen Versuchen (vielleicht hatte ich etwas falsch geschrieben?) und langer Recherche in den Foren fand ich heraus, dass es das &lt;a href="https://gitea.com/gitea/act_runner/issues/329" target="_blank" rel="noopener noreferrer" class="external-link"&gt;valid_volumes&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Attribut im &lt;code&gt;act&lt;/code&gt; gibt - taucht das Volume dort nicht auf, so wird es auch nicht eingebunden.&lt;/p&gt;&#10;&lt;p&gt;OK, also schnell der Action hinzugefügt:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# jekyll-build-action.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;container&lt;/span&gt;: &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;jvconseil/jekyll-docker:latest&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;valid_volumes&lt;/span&gt;: &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#39;**&amp;#39;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# This does not work. Also specialized lists indicating the volumes directly won&amp;#39;t work&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;: &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/tmp/blog_staging:/blog_staging&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/opt/cache:/opt/hostedtoolcache&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aber immer noch nichts. Immerhin kann ich in den Logs mit dem Wissen über &lt;code&gt;valid_volumes&lt;/code&gt; jetzt eine Warnung entdecken:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Deploy Jekyll site/build&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;/tmp/blog_staging&lt;span style="color:#f92672"&gt;]&lt;/span&gt; is not a valid volume, will be ignored&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Deploy Jekyll site/build&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;/opt/cache&lt;span style="color:#f92672"&gt;]&lt;/span&gt; is not a valid volume, will be ignored&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Etwas stimmt mit der Übergabe dieser Option nicht. Also recherchiere ich weiter. Es gibt &lt;a href="https://gitea.com/gitea/act_runner/issues/407" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Berichte&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, in denen das Anmelden der Volumes funktioniert. Also erzeuge ich nach &lt;a href="https://gitea.com/gitea/act_runner/src/branch/main#configuration" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Anleitung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; eine Konfigurationsdatei, wo ich das Folgende eintrage:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# runner/config.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Volumes (including bind mounts) can be mounted to containers. Glob syntax is supported, see https://github.com/gobwas/glob&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# You can specify multiple volumes. If the sequence is empty, no volumes can be mounted.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# For example, if you only allow containers to mount the `data` volume and all the json files in `/src`, you should change the config to:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# valid_volumes:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# - data&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# - /src/*.json&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# If you want to allow any volume, please use the following configuration:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# valid_volumes:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# - &amp;#39;**&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;valid_volumes&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;/tmp/blog_staging&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;/opt/hostedtoolcache&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Wichtig:&lt;/em&gt; Die Notation muss als String vorliegen, kommasepariert, und gibt stets die &lt;code&gt;Source&lt;/code&gt; des Volumes an. Also den Teil, der vor dem &lt;code&gt;:&lt;/code&gt; steht.&#10;Doch noch immer erscheint beim Bauen die Fehlermeldung &amp;ldquo;not a valid volume&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Doch dann fällt mir auf, dass die Config dem &lt;code&gt;act_runner&lt;/code&gt; selbst als Volume zur Verfügung gestellt werden muss - sonst kann der im Container laufende Runner ja gar nicht darauf zugreifen!&lt;/p&gt;&#10;&lt;p&gt;Daher sieht meine &lt;code&gt;docker-compose.yml&lt;/code&gt; für Gitea, Abschnitt &amp;ldquo;runner&amp;rdquo; nun wie folgt aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# gitea/docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runner&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gitea/act_runner:latest&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;environment&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;CONFIG_FILE=/config.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;GITEA_INSTANCE_URL=https://git.schallbert.de&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;GITEA_RUNNER_NAME=ichlaufe&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;GITEA_RUNNER_REGISTRATION_TOKEN= &amp;lt;redacted&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./runner/config.yml:/config.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./runner/data:/data&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/opt/hostedtoolcache:/opt/hostedtoolcache&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Und endlich:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | ls -al /blog_staging&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Deploy Jekyll site/build&lt;span style="color:#f92672"&gt;]&lt;/span&gt; | total &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Deploy Jekyll site/build&lt;span style="color:#f92672"&gt;]&lt;/span&gt; | drwxr-xr-x &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; root root &lt;span style="color:#ae81ff"&gt;4096&lt;/span&gt; Dec &lt;span style="color:#ae81ff"&gt;27&lt;/span&gt; 07:32 .&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Deploy Jekyll site/build&lt;span style="color:#f92672"&gt;]&lt;/span&gt; | drwxr-xr-x &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; root root &lt;span style="color:#ae81ff"&gt;4096&lt;/span&gt; Dec &lt;span style="color:#ae81ff"&gt;27&lt;/span&gt; 07:46 ..&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;Deploy Jekyll site/build&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ✅ Success - Main --- CHECK_VOLUME ---&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Was für ein Akt. Ich bin so froh, dass jetzt alles durchläuft und ich auf meinem lokalen Hostsystem tatsächlich die gebauten Dateien vorfinde! 🥳&lt;/p&gt;&#10;&lt;h2 id="reverse-proxy-connection-refused"&gt;Noch ein letzter Tipp&lt;/h2&gt;&#10;&lt;p&gt;Wenn ihr so wie ich mit einem Reverse Proxy arbeitet und so komische &lt;code&gt;connection refused&lt;/code&gt; Fehlermeldungen beim Hochfahren des Runners erhaltet wie:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker compose up&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;+&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Running 2/0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ✔ Container gitea Created 0.0s &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ✔ Container gitea-runner-1 Created 0.0s &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Attaching to gitea, gitea-runner-1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | level&lt;span style="color:#f92672"&gt;=&lt;/span&gt;info msg&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Starting runner daemon&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | level&lt;span style="color:#f92672"&gt;=&lt;/span&gt;error msg&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;fail to invoke Declare&amp;#34;&lt;/span&gt; error&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;unavailable: dial tcp &amp;lt;address&amp;gt;: connect: connection refused&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 | Error: unavailable: dial tcp &amp;lt;address&amp;gt; connect: connection refused&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea | Server listening on :: port 22.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gitea-runner-1 exited with code &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dann ist euer Reverse Proxy entweder falsch eingestellt oder - wie in meinem Falle - nicht hochgefahren.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Die Nutzung des SFTP (Secure File Transfer Protocol), also der Dateitransfer über ssh (secure shell), erhöht die Angriffsfläche meines Systems ein wenig. Der SSH-Dienst ist nämlich dann nicht nur für meine Action, sondern ganz allgemein von außen erreichbar.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>🗙 Gitea Actions - Jekyll Workflow</title><link>https://blog.schallbert.de/gitea-action-runner-native-jekyll/</link><pubDate>Thu, 30 Nov 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/gitea-action-runner-native-jekyll/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-11-30_ghaction-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: github actions workflow to build Jekyll page"&#10; title="🗙 Gitea Actions - Jekyll Workflow" /&gt;&#10;&lt;p&gt;Nachdem ich in &lt;a href="https://blog.schallbert.de/projects/move-blog-to-own-server/"&gt;diesem Projekt&lt;/a&gt; meinen eigenen, kleinen Server aufgesetzt und mit den entsprechenden Programmen zum Hosten meines Blogs ausgestattet habe, möchte ich ihn nun in die Lage versetzen, die Website automatisch zu bauen.&lt;/p&gt;&#10;&lt;h2 id="gewünschter-ablauf"&gt;Gewünschter Ablauf&lt;/h2&gt;&#10;&lt;p&gt;Ich möchte folgendes erreichen: Sobald Gitea eine &lt;code&gt;push&lt;/code&gt;-Aktion auf dem Blog-Repository verzeichnet, soll ein Action Runner in einem eigenen Docker-Container loslaufen und Ruby, Bundler, Jekyll sowie sämtliche Abhängigkeiten nachladen. Dann soll der Runner meine Quelldateien für den Blog kopieren und dem &lt;code&gt;bundle exec jekyll build&lt;/code&gt; Prozess zur Verfügung stellen, welcher dann die Website baut und schließlich im &lt;code&gt;_site&lt;/code&gt;-Ordner ablegt. Anschließend soll der Runner sich selbst beenden und alle in Anspruch genommenen Ressourcen wieder freigeben.&lt;/p&gt;&#10;&lt;p&gt;An dieser Stelle möchte ich nachkontrollieren können, ob die Website fehlerfrei gebaut wurde und in Zukunft eventuell noch ein paar weitere Prüfungen darüberlaufen lassen - z.B. kaputte Links erkennen, nicht angezeigte Bilder markieren, Rechtschreibprüfung durchführen.&lt;/p&gt;&#10;&lt;p&gt;Die Freigabe sowie die Veröffentlichung soll dann ein anderer Prozess übernehmen, um den ich mich später kümmere.&lt;/p&gt;&#10;&lt;h2 id="ausgangspunkt-github-actions"&gt;Ausgangspunkt: Github Actions&lt;/h2&gt;&#10;&lt;p&gt;Wie üblich wildere ich erst einmal bei anderen Menschen, die ein ähnliches Problem für sich bereits gelöst haben. Ich wurde im &lt;a href="https://github.com/actions/starter-workflows/blob/main/pages/jekyll.yml" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Github Actions starter-workflows&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Repository von Github selbst fündig und habe die Workflowdatei für Jekyll mit leichten Modifikationen direkt mal mit meiner Gitea-Instanz ausprobiert:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# jekyll-build-pages.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Sample workflow for building and deploying a Jekyll site&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Deploy Jekyll site&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;run-name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${{ gitea.actor }} builds Jekyll site&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;on&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;push]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Build job&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;build&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runs-on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ubuntu-latest&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Checkout&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Setup Ruby&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf&lt;/span&gt; &lt;span style="color:#75715e"&gt;# v1.146.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ruby-version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;3.1&amp;#39;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Not needed with a .ruby-version file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;bundler-cache&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt; &lt;span style="color:#75715e"&gt;# runs &amp;#39;bundle install&amp;#39; and caches installed gems automatically&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cache-version&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Increment this number if you need to re-download cached gems&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#[...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nachdem ich diesen Workflow in mein Blogverzeichnis unter &lt;code&gt;workflows/jekyll.yml&lt;/code&gt; hochgeladen hatte, lief der Runner tatsächlich direkt los:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-11-30_rubyfails.jpg" alt="Image: Gitea action runner returns error on ruby install" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="probleme-mit-der-ruby-installation"&gt;Probleme mit der Ruby-Installation&lt;/h2&gt;&#10;&lt;p&gt;Schon mal prima. Allerdings wenig erbaulich, dass bereits bei der Installation von Ruby etwas nicht passt. Werfen wir einen Blick in die Logs:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;::error::The current runner (debian-11-x64) was detected as self-hosted because the platform does not match a GitHub-hosted runner image &#10;(or that image is deprecated and no longer supported).&#10;In such a case, you should install Ruby in the $RUNNER_TOOL_CACHE yourself, for example using https://github.com/rbenv/ruby-build&#10;You can take inspiration from this workflow for more details: &#10;https://github.com/ruby/ruby-builder/blob/master/.github/workflows/build.yml&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Na, das ist doch mal eine hilfreiche Fehlermeldung!&lt;/p&gt;&#10;&lt;p&gt;Der Runner läuft um Ressourcen zu sparen standardmäßig im Docker mit einem abgespeckten Image basierend auf Debian namens &lt;code&gt;node:16-bullseye&lt;/code&gt; &lt;a href="https://github.com/nektos/act/blob/master/IMAGES.md" target="_blank" rel="noopener noreferrer" class="external-link"&gt;(Referenz)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Scheinbar ist dieser nicht kompatibel mit dem unter Ubuntu22 betriebenen Github Actions Runner. Ich habe also ein paar Lösungsmöglichkeiten zur Verfügung:&lt;/p&gt;&#10;&lt;h3 id="möglichkeiten-zur-fehlerbehebung"&gt;Möglichkeiten zur Fehlerbehebung&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Den Runner über seine &amp;ldquo;Labels&amp;rdquo; auf Ubuntu22.04 umsatteln.&lt;/li&gt;&#10;&lt;li&gt;Ren Runner nicht im Docker, sondern nativ auf dem Server laufen lassen.&lt;/li&gt;&#10;&lt;li&gt;Wie in der Fehlermeldung vorgeschlagen, Ruby im Cache des Runners vorinstallieren.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/"&gt;Ein Jekyll-Dockerimage verwenden&lt;/a&gt;, welches Jekyll bereits im Gepäck hat und nicht erst die Installation benötigt.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="den-runner-mit-ubuntu22-statt-node16-betreiben"&gt;Den Runner mit Ubuntu22 statt Node16 betreiben&lt;/h3&gt;&#10;&lt;p&gt;Das entsprechende &lt;a href="https://hub.docker.com/_/ubuntu" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Dockerimage&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; existiert zwar. Aber es kostet aber einen Haufen Speicherplatz auf meinem Server, benötigt durch seinen größeren Funktionsumfang mehr RAM und braucht vor allem länger, bis es hochgefahren ist.&lt;/p&gt;&#10;&lt;p&gt;Und Zeit ist bei Runnern wertvoll, schließlich will ich ja möglichst schnell wissen, ob das Bauen geklappt hat.&lt;/p&gt;&#10;&lt;h3 id="den-runner-direkt-auf-der-hostmaschine-ubuntu22-laufen-lassen"&gt;Den Runner direkt auf der Hostmaschine (Ubuntu22) laufen lassen&lt;/h3&gt;&#10;&lt;p&gt;Ist sicherheitstechnisch nicht so sehr zu empfehlen (Bei &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches" target="_blank" rel="noopener noreferrer" class="external-link"&gt;einem ungeschützten Branch&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; würde der Runner von Dritten per &lt;code&gt;git push --force&lt;/code&gt; hochgeschobenen Schadcode unbesehen auf meiner Hostmaschine ausführen) und zudem ginge die Portabilität flöten, weil ich das Zielsystem des Runners dann fest verdrahte.&lt;/p&gt;&#10;&lt;h3 id="gitea-act_runner-direkt-auf-dem-host-laufen-lassen"&gt;Gitea act_runner direkt auf dem Host laufen lassen&lt;/h3&gt;&#10;&lt;p&gt;Für den zweiten Ansatz habe ich mir per &lt;a href="https://docs.gitea.com/1.23/usage/actions/act-runner/#configuration" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Anleitung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; eine Konfiguration für den &lt;code&gt;runner&lt;/code&gt; angelegt und darin das bestehende label-mapping so geändert, dass nicht mehr in Richtung Docker, sondern auf dem Ubuntu des Servers selbst gebaut wird. Dafür genügt es in meinem Fall, das Label &lt;code&gt;ubuntu-latest:host&lt;/code&gt; statt &lt;code&gt;ubuntu-latest&lt;/code&gt; in die Konfiguration zu schreiben. Leider erhielt ich hier Fehlermeldungen bei den Zugriffsrechten des Runners, welcher aus Docker heraus auf dem Hostsystem laufen müsste.&lt;/p&gt;&#10;&lt;p&gt;Nicht so schlimm, ich wollte diese Lösung wegen der oben beschriebenen möglichen Probleme sowieso nicht dauerhaft verwenden. Also investiere ich hier keine weitere Zeit.&lt;/p&gt;&#10;&lt;h3 id="ruby-im-cache-vorinstallieren"&gt;Ruby im Cache vorinstallieren&lt;/h3&gt;&#10;&lt;p&gt;Wie von der Fehlermeldung empfohlen habe ich &lt;a href="https://about.gitea.com/resources/tutorials/enable-gitea-actions-cache-to-accelerate-cicd" target="_blank" rel="noopener noreferrer" class="external-link"&gt;dieser Anleitung folgend&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; in der &lt;code&gt;docker-compose.yml&lt;/code&gt; von Gitea ein Volume angelegt für den Tool Cache des runners und das Zielverzeichnis in der Umgebungsvariable &lt;code&gt;RUNNER_TOOL_CACHE&lt;/code&gt; abgelegt.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# gitea/docker-compose.yml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# [...]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;runner&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gitea/act_runner:nightly&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;environment&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;GITEA_INSTANCE_URL=&amp;lt;redacted&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;GITEA_RUNNER_NAME=&amp;lt;redacted&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;RUNNER_TOOL_CACHE=/opt/hostedtoolcache&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;GITEA_RUNNER_REGISTRATION_TOKEN= &amp;lt;redacted&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./runner/data:/data&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/opt/hostedtoolcache:/opt/hostedtoolcache&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hiermit wird dem in Docker laufenden Runner der Ordner &lt;code&gt;/opt/hostedtoolcache&lt;/code&gt; im Dateisystem des Hosts unter dem Namen &lt;code&gt;/opt/hostedtoolcache&lt;/code&gt; zur Verfügung gestellt.&#10;Nun probiere ich mal, Ruby direkt dort hinein zu installieren.&lt;/p&gt;&#10;&lt;p&gt;Also lade ich das &lt;a href="https://github.com/rbenv/ruby-build" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;code&gt;ruby-build&lt;/code&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; repository herunter (&lt;code&gt;git clone&lt;/code&gt;) und lasse das Installerscript mit &lt;code&gt;./ruby-build/install.sh&lt;/code&gt; durchlaufen.&#10;Danach führe ich den Build mit dem in der Fehlermeldung angegebenen Pfad aus: &lt;code&gt;ruby-build 3.1.4 /opt/hostedtoolcache/Ruby/3.1.4/x64&lt;/code&gt;&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;==&amp;gt; Downloading openssl-3.1.4.tar.gz...&#10; % Total % Received % Xferd Average Speed Time Time Time Current&#10; Dload Upload Total Spent Left Speed&#10;100 14.8M 100 14.8M 0 0 81.3M 0 --:--:-- --:--:-- --:--:-- 81.1M&#10;==&amp;gt; Installing openssl-3.1.4...&#10;&#10;BUILD FAILED (Ubuntu 22.04 on x86_64 using ruby-build 20231114)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="probleme-mit-prebuilt-ruby"&gt;Probleme mit prebuilt-Ruby&lt;/h3&gt;&#10;&lt;p&gt;Das Log zeigt das Problem:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[...] No C compiler found, please specify one with the environment variable CC [...]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Also tippe ich &lt;code&gt;apt install build-essential&lt;/code&gt; und stelle dann mit &lt;code&gt;which gcc&lt;/code&gt; fest, dass er nun unter &lt;code&gt;/usr/bin/gcc&lt;/code&gt; installiert ist. Auf ein Neues!&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;crypto/comp/c_zlib.c:36:11: fatal error: zlib.h: No such file or directory&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dann schnell &lt;code&gt;apt install libz-dev&lt;/code&gt; eingeben und erneut probieren. Dieses mal läuft er recht lange, dann:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;*** Following extensions are not compiled:&#10;openssl:&#10; Could not be configured. It will not be installed.&#10; /tmp/ruby-build.20231205114312.9174.0gmhNf/ruby-3.1.4/ext/openssl/extconf.rb:100: OpenSSL library could not be found. You might Check ext/openssl/mkmf.log for more details.&#10;readline:&#10; Could not be configured. It will not be installed.&#10; /tmp/ruby-build.20231205114312.9174.0gmhNf/ruby-3.1.4/ext/readline/extconf.rb:62: Neither readline nor libedit was found&#10; Check ext/readline/mkmf.log for more details.&#10;*** Fix the problems, then remove these directories and try again if you want.&#10;[...]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;OK, nun ist es openssl. Fix nachinstallieren mit &lt;code&gt;apt install libssl-dev&lt;/code&gt; sowie &lt;code&gt;apt install libreadline-dev&lt;/code&gt; und endlich 🎉:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;Installed ruby-3.1.4 to /opt/hostedtoolcache/Ruby/3.1.4/x64&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="noch-immer-fehler-beim-bauen-mit-dem-github-acitons-script"&gt;Noch immer Fehler beim Bauen mit dem Github-Acitons script&lt;/h2&gt;&#10;&lt;p&gt;Merkwürdig, das Log spuckt mir immer noch denselben Fehler aus:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;💬 ::debug::isExplicit: 3.1.4&#10;&#10;gitea-runner-1 | [Deploy Jekyll site/build] 💬 ::debug::checking cache: /opt/hostedtoolcache/Ruby/3.1.4/x64&#10;gitea-runner-1 | [Deploy Jekyll site/build] | ::debug::checking cache: /opt/hostedtoolcache/Ruby/3.1.4/x64&#10;gitea-runner-1 | [Deploy Jekyll site/build] 💬 ::debug::not found&#10;gitea-runner-1 | [Deploy Jekyll site/build] | ::debug::not found&#10;gitea-runner-1 | [Deploy Jekyll site/build] ❗ ::error::The current runner (debian-11-x64) was detected as self-hosted&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Das geht auf folgende Abfage in der ausführenden &lt;a href="https://github.com/ruby/setup-ruby/blob/master/ruby-builder.js#L97" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Action&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zurück:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// setup-ruby/ruby-builder.js&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;common&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;shouldUseToolCache&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;engine&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;version&lt;/span&gt;)) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;inToolCache&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;common&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;toolCacheFind&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;engine&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;version&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;inToolCache&lt;/span&gt;) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;rubyPrefix&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;inToolCache&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;toolCacheRubyPrefix&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;common&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;getToolCacheRubyPrefix&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;platform&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;engine&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;version&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;common&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;isSelfHostedRunner&lt;/span&gt;()) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rubyBuildDefinition&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;engine&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;ruby&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;?&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;version&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;engine&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;version&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;core&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;( [...] )&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="update-jan-2024"&gt;Update Jan-2024&lt;/h3&gt;&#10;&lt;p&gt;Es es gibt zwei weitere Möglichkeiten dafür, dass der &lt;code&gt;toolcache&lt;/code&gt; nicht gefunden wird:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Ich habe das Volume nicht der Action, sondern dem Runner zur Verfügung gestellt. Ich bin mir nicht sicher, dass die Daten auch in der Action verfügbar sind.&lt;/li&gt;&#10;&lt;li&gt;Der &lt;code&gt;toolcache&lt;/code&gt;-Pfad ist nicht in der Konfigurationsdatei des runners unter dem Eintrag &lt;code&gt;valid_volumes&lt;/code&gt; enthalten. Eine &lt;a href="https://gitea.com/gitea/act/pulls/60" target="_blank" rel="noopener noreferrer" class="external-link"&gt;pull request Kommunikation&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; lässt mich jedoch vermuten, dass dies automatisch passiert. Dennoch bin ich mir nicht 100% sicher, ob die Information noch dem Stand der Dinge entspricht.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="docker-volume-überprüfen"&gt;Docker volume überprüfen&lt;/h3&gt;&#10;&lt;p&gt;Mist. Prüfen wir schnell mal, ob Ruby in dem Volume auch wirklich verfügbar ist. Dafür suchen wir mit &lt;code&gt;docker ps&lt;/code&gt; die Container ID des runners heraus und geben sie bei folgendem Kommando ein:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec -it &amp;lt;containerID&amp;gt; bash&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dann navigiere ich in den gesuchten Ordner &lt;code&gt;cd /opt/hostedtoolcache&lt;/code&gt;, liste dort den Inhalt mit &lt;code&gt;ls&lt;/code&gt; und siehe da: &lt;code&gt;Ruby&lt;/code&gt;. Es liegt also alles vor, nur wird es nicht gefunden.&lt;/p&gt;&#10;&lt;h2 id="misserfolg-anerkennen"&gt;Misserfolg anerkennen&lt;/h2&gt;&#10;&lt;p&gt;So komme ich hier also nicht weiter 😖. Daher öffne ich mein Problem für die Community unter dem Titel &lt;a href="https://gitea.com/gitea/act_runner/issues/441" target="_blank" rel="noopener noreferrer" class="external-link"&gt;act_runner cannot find hostedtoolcache&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und probiere&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/gitea-action-runner-jekyll-dockerimage/"&gt;einen anderen Ansatz&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 id="problemursache"&gt;Problemursache&lt;/h3&gt;&#10;&lt;p&gt;Der Support der Action &lt;code&gt;setup-ruby&lt;/code&gt; für selbsgehostete CI-Pipelines wie meinen &lt;code&gt;act_runner&lt;/code&gt; wurde &lt;a href="https://github.com/ruby/setup-ruby/issues/475#issuecomment-1455099634" target="_blank" rel="noopener noreferrer" class="external-link"&gt;wohl eingestellt&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Die Dokumentation mit den Hinweisen zur Fehlerbehebung bei Verwendung selbstgehosteter Runner allerdings zum Zeitpunkt der Verwendung durch meine Wenigkeit war aber noch nicht auf Stand gebracht worden.&lt;/p&gt;&#10;</description></item><item><title>Mehrsprachige Webseiten - l10n</title><link>https://blog.schallbert.de/jekyll-polyglot-language-support/</link><pubDate>Tue, 14 Nov 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/jekyll-polyglot-language-support/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-11-14_translations_polyglot_flags-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Flags that can be clicked on to change display language"&#10; title="Mehrsprachige Webseiten - l10n" /&gt;&#10;&lt;p&gt;Die Bereitstellung einer Webseite in mehreren Sprachen wird &amp;ldquo;internationalization&amp;rdquo; - kurz &lt;a href="https://en.wikipedia.org/wiki/Internationalization_and_localization" target="_blank" rel="noopener noreferrer" class="external-link"&gt;i16n&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; beziehungsweise &amp;ldquo;localization&amp;rdquo; &lt;code&gt;l10n&lt;/code&gt; genannt. Sucht man nach diesen Begriffen im Netz, findet man eine Menge tollen Materials zum Thema. Die Zahlen in den Kurzformen dieser Begriffe geben die Anzahl Buchstaben in der Wortmitte an.&lt;/p&gt;&#10;&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;&#10;&lt;p&gt;Ich bin schon vor langer Zeit gefragt worden, ob ich nicht mal eine &lt;a href="https://hobbyline.info/forum/index.php?thread/603-blog-artikel-zur-portalfr%C3%A4se/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;deutsche Version dieser Webseite&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; anfertigen wolle. Bis dato hielt ich das für &lt;a href="https://hobbyline.info/forum/index.php?thread/603-blog-artikel-zur-portalfr%C3%A4se/&amp;amp;postID=7619#post7619" target="_blank" rel="noopener noreferrer" class="external-link"&gt;zu viel Arbeit&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Dies änderte sich erst vor gut einem halben Jahr, als ich dieselbe Anfrage verbal von mehreren Leuten unabhängig voneinander in kurzer Zeit erhielt.&lt;/p&gt;&#10;&lt;p&gt;Also investierte ich einen Haufen freier Abende in die Fleißarbeit, rund 80 Artikel auf Deutsch zu übertragen. Doch ich war noch immer nicht fertig.&lt;/p&gt;&#10;&lt;h2 id="jekyll-plugin"&gt;Jekyll plugin&lt;/h2&gt;&#10;&lt;p&gt;Ich sah mich nach Möglichkeiten um, meinen Internetauftritt mit Jekyll mehrsprachig zu gestalten. Dabei fand ich unter Anderem &lt;a href="https://demondehellis.github.io/en/posts/how-to-make-jekyll-blog-multilingual/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;demondehellis&amp;rsquo; plugin-freie Lösung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://polyglot.untra.io/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;untras polyglot plugin&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://github.com/kurtsson/jekyll-multiple-languages-plugin" target="_blank" rel="noopener noreferrer" class="external-link"&gt;kurtssons jekyll-multiple-languages-plugin&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und noch ein paar weitere Möglichkeiten, diesen Webauftritt in mehreren Sprachen anzubieten.&lt;/p&gt;&#10;&lt;p&gt;Nun musste ich mich für eine entscheiden. Die plugin-freie Lösung fiel recht schnell heraus, da ich &amp;ldquo;gleiche&amp;rdquo; Posts verschiedener Sprache gleich benennen wollte und damit Interlinks zwischen Beiträgen unabhängig von der verwendeten Sprache bestehen bleiben sollten - irgendwas muss also dafür sorgen, dass sich die Sprachen nicht wild mischen.&lt;/p&gt;&#10;&lt;p&gt;Blieben die Plugins übrig. Kurz gesagt stieß ich zufällig auf &lt;a href="https://leo3418.github.io/collections/multilingual-jekyll-site/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;leo3418s&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; hervorragende Artikelserie für die Implementierung von Polyglot - und so entschied ich mich für ebendieses Plugin.&lt;/p&gt;&#10;&lt;h3 id="polyglot-installation"&gt;Polyglot: Installation&lt;/h3&gt;&#10;&lt;p&gt;Die Installation von Polyglot war einfach, denn sowohl die &lt;a href="https://github.com/untra/polyglot" target="_blank" rel="noopener noreferrer" class="external-link"&gt;im Repository&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; bereitgestellte &lt;code&gt;readme.md&lt;/code&gt; als auch der von leo3418 geschriebene &lt;a href="https://leo3418.github.io/collections/multilingual-jekyll-site/set-up-polyglot.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Blogartikel&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; beschäftigen sich ausführlich mit den ersten Schritten. In der Tat fand ich die Dokumentation so gut, dass ich darauf verweisen möchte und hier gar nicht weiter darüber berichten werde.&lt;/p&gt;&#10;&lt;h3 id="restructuring-the-website"&gt;Umstrukturierung der Website&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-11-14_folderstructure.jpg" alt="image: updated folder structure for this site" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Bei Polyglot gibt es zwei Möglichkeiten, die Ordnerstruktur für die Website auf mehrsprachig umzustellen:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Man benutzt das Sprachkürzel im Artikelnamen und ermöglicht so eine Zuordnung. Dieser Artikel hier würde dann z.B. heißen: &lt;code&gt;2023-11-14-jekyll-polyglot-language-support-de.md&lt;/code&gt; und analog in jeder weiteren Sprache mit einem anderen Sprachkürzel&lt;/li&gt;&#10;&lt;li&gt;Man erstellt Ordner mit dem Sprachkürzel, z.b. &lt;code&gt;/de&lt;/code&gt;, behält aber identische Dateinamen für verschiedene Sprachen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Ich habe mich für letzteres entschieden, um der Umbenennung meiner schon vorhandenen Artikel zu entgehen. Die Sprache wird von Polyglot auch bei Verwendung von Interlinks auf der Webseite beibehalten. Klickt man z.B. auf den Artikel &lt;a href="https://blog.schallbert.de/milling-small-parts/"&gt;https://schallbert.de/milling-small-parts/&lt;/a&gt;, so wird Polyglot ausgehend von der Sprache des aktuellen Artikels auch den neuen Artikel in dieser Sprache aufrufen. Einzig bei Überschriften von Abschnitten wird es schwierig: Die sind nämlich übersetzt. Also wird der Link auf die englische Version der Seite für die Überschrift &lt;a href="https://blog.schallbert.de/jekyll-polyglot-language-support/#restructuring-the-website"&gt;#Umstrukturierung-der-Website&lt;/a&gt; in einem 404-Fehler enden.&lt;/p&gt;&#10;&lt;p&gt;Daher muss man für die Überschriften den kompletten Artikelpfad inkl. der Lokalisierung angeben. In diesem Beispiel wäre folglich &lt;a href="https://blog.schallbert.de/jekyll-polyglot-language-support/#restructuring-the-website"&gt;/posts/en/jekyll-polyglot-language-support/#restructuring-the-website&lt;/a&gt; der korrekte Interlink.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: Alternativ kann man der Überschrift auch eine sprachunabhängige &amp;ldquo;ID&amp;rdquo; geben, sodass auch hier die Anchor-Links auf die Überschriften identisch bleiben. Beispiel: &lt;code&gt;## Umstrukturierung der Website {# website-restructuring}&lt;/code&gt;, Aufruf im Link per &lt;code&gt;#website-restucturing&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="übersetzung-von-seitentitel-navigation-kopf--und-fußzeile"&gt;Übersetzung von Seitentitel, Navigation, Kopf- und Fußzeile&lt;/h3&gt;&#10;&lt;p&gt;Hier wird es ein wenig komplizierter. Die &lt;code&gt;_config.yml&lt;/code&gt; von Jekyll lässt ohne Weiteres nicht zu, dass mehrere Varianten von ihr existieren. Man benötigt für sprachsensitive Parameter wie &lt;code&gt;site.title&lt;/code&gt; oder &lt;code&gt;site.description&lt;/code&gt; in der config daher die Möglichkeit, die Standardwerte zu überschreiben. Meine Methode das zu lösen war, im &lt;code&gt;_data&lt;/code&gt;-Ordner eine Datei mit Namen &lt;code&gt;l10n.yml&lt;/code&gt; anzulegen, in der dann diese Parameter abgelegt sind. Um sie dann auch korrekt anzuzeigen, sind die &lt;code&gt;html&lt;/code&gt;-Dateien im &lt;code&gt;_includes&lt;/code&gt;-Ordner so anzupassen, dass Parameter dieser Datei statt der config eingelesen und durch die Templatesprache Liquid dann in html umgesetzt werden. Beispiel: &lt;code&gt;{{ site.data.ui-text[site.data.l10n.locale].follow_label | remove: &amp;quot;:&amp;quot; | default: &amp;quot;Follow&amp;quot; }}&lt;/code&gt;. Hier wird dann wie im Bild gezeigt dann in der deutschen Version &lt;strong&gt;FOLGEN:&lt;/strong&gt; angezeigt und in der englischen &lt;strong&gt;FOLLOW:&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;h3 id="sprachauswahl"&gt;Sprachauswahl&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-11-14_translations_polyglot_flags.jpg" alt="image: footer section of this website" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Natürlich möchte ich die Möglichkeit geben, die Sprache der Website selbst zu wählen. Ich habe mich hier für das Sprachkürzel sowie die entsprechende Flagge entschieden, welche im unteren Bereich einer jeden Seite eingeblendet werden. Klickt man hier drauf, so wird die Seite in der entsprechenden Sprache neu geladen. Dafür war es notwendig, einen &amp;ldquo;language-switcher&amp;rdquo; in Liquid zu schreiben.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// _includes/language-switcher.html&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;%&lt;/span&gt; raw &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; lang in site.languages &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; assign url_parts &lt;span style="color:#f92672"&gt;=&lt;/span&gt; page.url &lt;span style="color:#f92672"&gt;|&lt;/span&gt; split: &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; assign lang_code &lt;span style="color:#f92672"&gt;=&lt;/span&gt; lang &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; assign collection_name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; url_parts[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; assign post_url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; url_parts &lt;span style="color:#f92672"&gt;|&lt;/span&gt; last &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; lang &lt;span style="color:#f92672"&gt;==&lt;/span&gt; site.default_lang &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; collection_name &lt;span style="color:#f92672"&gt;==&lt;/span&gt; post_url &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; assign lang_url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; append: post_url &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; assign lang_url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; append: collection_name &lt;span style="color:#f92672"&gt;|&lt;/span&gt; append: &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; append: post_url &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; endif &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; url_parts.size &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; assign lang_url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; append: lang_code &lt;span style="color:#f92672"&gt;|&lt;/span&gt; append: &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; collection_name &lt;span style="color:#f92672"&gt;==&lt;/span&gt; post_url &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; assign lang_url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; lang_url &lt;span style="color:#f92672"&gt;|&lt;/span&gt; append: post_url &lt;span style="color:#f92672"&gt;%&lt;/span&gt;} &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; assign lang_url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; lang_url &lt;span style="color:#f92672"&gt;|&lt;/span&gt; append: collection_name &lt;span style="color:#f92672"&gt;|&lt;/span&gt; append: lang_url &lt;span style="color:#f92672"&gt;|&lt;/span&gt; append: post_url &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; endif &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;%&lt;/span&gt; endif &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;a href&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34; {{ lang_url }}&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;{{ site.data[lang].l10n.lang_name }}&lt;span style="color:#f92672"&gt;&amp;lt;/&lt;/span&gt;a&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;%&lt;/span&gt; endfor &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;%&lt;/span&gt; endraw &lt;span style="color:#f92672"&gt;%&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hierfür wird die aktuelle &lt;code&gt;url&lt;/code&gt; am Slash in Einzelteile zerlegt. Anschließend der letzte Teil der url als Artikelname &lt;code&gt;post_url&lt;/code&gt; gesetzt und angenommen, dass der erste Teil der URL der collection entspricht, z.B. &lt;code&gt;posts-hardware&lt;/code&gt;. Die anschließende Logik setzt die Sprachkürzel je nach Bedarf in die url ein, sodass je nach Verwendung einer collection oder ohne die entsprechende Zielurl korrekt erzeugt wird.&lt;/p&gt;&#10;&lt;h2 id="veröffentlichung"&gt;Veröffentlichung&lt;/h2&gt;&#10;&lt;p&gt;Ich war echt stolz auf diesen Umbau. Ich finde, dass sich alles gut einfügt und ich mit der Zweisprachigkeit meinen Nutzerkreis gut erweitern kann. Der Nachteil für mich ist die zusätzliche Übersetzungsarbeit, die von nun an bei jedem Artikel auf mich zukommt.&lt;/p&gt;&#10;&lt;p&gt;Als ich diese Seite das erste mal auf die Github Page hochlud, merkte ich, dass das so überhaupt nicht funktioniert. Github Pages unterstützt Polyglot nämlich nicht, sodass plötzlich alle Artikel unabhängig von ihrer Sprache auf einmal auf meiner Website erscheinen.&lt;/p&gt;&#10;&lt;p&gt;Nun blieben mir nur zwei Möglichkeiten: &lt;a href="https://blog.schallbert.de/struggling-github-actions/"&gt;Eigene Github Actions&lt;/a&gt; für das Deployment aufzusetzen - oder ein Umzug auf einen eigenen Server. Ich habe mich für Letzteres entschieden, da die Github-Server für diese Seite meines Wissens nach in UK stehen und und ich mit einem Server in Deutschland lieber auf der sicheren Seite wäre.&lt;/p&gt;&#10;&lt;p&gt;Der eigentliche Umbau ist aber deutlich zu groß, um ihn in diesem Post abzuhandeln - dafür gibt es ein &lt;a href="https://blog.schallbert.de/projects/move-blog-to-own-server/"&gt;eigenes Softwareprojekt&lt;/a&gt;.&lt;/p&gt;&#10;</description></item><item><title>Kleinteile fräsen</title><link>https://blog.schallbert.de/milling-small-parts/</link><pubDate>Wed, 01 Nov 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/milling-small-parts/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-11-01_smallparts-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Milled small part, size comparison to a coin"&#10; title="Kleinteile fräsen" /&gt;&#10;&lt;h2 id="warum-kleine-teile-überhaupt-fräsen"&gt;Warum kleine Teile überhaupt fräsen?&lt;/h2&gt;&#10;&lt;p&gt;Es ist meist schwierig, sehr kleine Teile zu fertigen, zudem wenn sie nur in geringer Stückzahl benötigt werden. Spritzgießen lohnt sich hier nicht und 3D-Drucker wie sie im Hobbybereich eingesetzt werden sind erfüllen nicht die Toleranzanforderungen für meine Passungen. Die Laserschneider, auf die ich Zugriff habe, können das Teil ebenfalls nicht fertigen, weil es verschiedene Oberflächenhöhen hat. Auf die Option, das Teil in Fernost anfertigen zu lassen, möchte ich verzichten.&lt;/p&gt;&#10;&lt;p&gt;Dennoch: Diese Teile können auf einer Fräsmaschine gefertigt werden. Dieses Video zeigt, wie:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/64TS5d5cp2eNqf8SSPvY77" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Kleinteile fräsen&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="welche-ausstattung-muss-die-maschine-dafür-besitzen"&gt;Welche Ausstattung muss die Maschine dafür besitzen?&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Einen Vakuumtisch&lt;/li&gt;&#10;&lt;li&gt;Eine Vakuumpumpe, vorzugsweise mit hohen Unterdrücken&lt;/li&gt;&#10;&lt;li&gt;Etwas Frischhaltefolie&lt;/li&gt;&#10;&lt;li&gt;Ein Vakuumvlies&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="wofür-ist-dieses-kleinteil"&gt;Wofür ist dieses Kleinteil?&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-11-01_smallparts_soapbutler.jpg" alt="Image: Video splash preview with soapbutler" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Das im Video gefertigte Teil gehört zu einem meiner &lt;a href="https://blog.schallbert.de/projects/seifenbutler/"&gt;Seifenbutler&lt;/a&gt;. Die aktuelle Version kann an jeder handelsüblichen Duschstange mit Durchmesser von &lt;code&gt;18-25mm&lt;/code&gt; befestigt werden und benötigt dieses Teilchen zum Klemmen. Es drückt sich an der Rückseite an die Stange und sorgt so dafür, dass der Seifenbutler weder verrutscht noch wackelt.&lt;/p&gt;&#10;&lt;p&gt;Mit diesem Upgrade gegenüber früheren Versionen ist der Seifenbutler nicht nur in der Lage, festes Shampoo zu halten, sondern auch die viel schwereren Shampooflaschen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-11-01_soapbutler_shampoobottle.jpg" alt="Image: black soapbutler holding a shampoo bottle" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Der &amp;ldquo;Klemmer&amp;rdquo; muss ohne merkliches Spiel in seine Führung passen und dennoch leichtgängig zu verschieben sein, um sich an verschiedene Duschstangendurchmesser anpassen zu können.&lt;/p&gt;&#10;&lt;h2 id="was-muss-noch-beachtet-werden"&gt;Was muss noch beachtet werden?&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Frischhaltefolie: Wenn Teile ausgeschnitten werden, sollten sie nicht zu groß sein. Ansonsten wird die Folie vom Fräser mitgerissen und hinterlässt hässliche Spuren im Werkstück. Außerdem kann sie eine Unwucht erzeugen, die bei hohen Drehzahlen zu Beschädigung des Fräsers oder sogar der Maschine führen kann. Um das Einziehen zu vermeiden hilft das Anbringen eines kleinen Stückes doppelseitigen Klebebandes auf dem auszuschneidenden Teil.&lt;/li&gt;&#10;&lt;li&gt;Vakuumvlies: Das Vlies wird bei Anlegen des Vakuums auf weniger als die Hälfte seiner Höhe zusammengepresst. Es ist also erforderlich, die Werkstückhöhe bei angelegtem Vakuum abzunullen.&lt;/li&gt;&#10;&lt;li&gt;Nicht ganz durchfräsen sondern eine dünne Materialschicht stehen lassen. Ihre Stärke hängt vom bearteiten Material ab. Bei PMMA lasse ich &lt;code&gt;0.3mm&lt;/code&gt; stehen, bei Holz &lt;code&gt;0.7mm&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Ich berücksichtige die Frischhaltefolie (etwa &lt;code&gt;10µm&lt;/code&gt; stark) nicht beim Einmessen der Werkstückhöhe, da sie sich in der Größenordnung meiner Maschinentoleranz befindet. Dickere Schutzfolien (&lt;code&gt;20-30µm&lt;/code&gt;), wie sie z.B. bei Plexiglas und Dibond verwendet werden, hingegen schon - zumal sie doppelseitig aufgebracht sind.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;</description></item><item><title>DIY Kantenfinder</title><link>https://blog.schallbert.de/touching-off/</link><pubDate>Sun, 21 May 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/touching-off/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-05-21_edgefinder-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: DIY low-cost edge finder"&#10; title="DIY Kantenfinder" /&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-05-21_edge_taperedtool.jpg" alt="Image: getting XY0 of a workpiece visually" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Als CNC-Bediener muss ich bei manchen Arbeiten genau wissen, wo sich mein Werkstück befindet. Daher benötige ich in solchen Fällen die &lt;code&gt;X/Y&lt;/code&gt;-Werte einer Ecke des Werkstücks im Maschinenkoordinatensystem. Bisher habe ich dieses Problem mittels eines V-Fräsers gelöst, dessen Spitze ich manuell auf die Kante des Werkstücks verfahren und immer wieder mit Augenmaß geprüft habe, ob die Positionen übereinstimmen.&lt;/p&gt;&#10;&lt;h2 id="das-problem"&gt;Das Problem&lt;/h2&gt;&#10;&lt;p&gt;Obwohl diese manuelle Methode relativ schnell und unkompliziert anzuwenden ist, so ist sie für die meisten Aufträge nicht mehr genau genug. Als ich in die doppelseitige Bearbeitung eingestiegen bin &lt;a href="https://blog.schallbert.de/cut-dibond/"&gt;wie damals mit Dibond&lt;/a&gt; war der Versatz so groß, dass ich die ersten Teile nur noch entsorgen konnte.&lt;/p&gt;&#10;&lt;h2 id="lösung"&gt;Lösung&lt;/h2&gt;&#10;&lt;p&gt;Ich habe mein Problem mit dem Hersteller meiner Fräsmaschine besprochen - in der Annahme, dass ich einen elektrischen, in die Spindel einzuspannenden Kantentaster &amp;ldquo;3D finder&amp;rdquo; empfohlen bekommen würde und nun wieder viel Geld und Arbeit in Kauf und Einrichtung eines solchen Messwerkzeugs fließen müsse. Da lag ich aber falsch. Stattdessen erhielt ich einen Tipp, wie ich einen einfachen aber genauen Kantenfinder bauen kann, der zudem praktisch nichts kostet.&lt;/p&gt;&#10;&lt;h3 id="einzelteile"&gt;Einzelteile&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-05-21_build.jpg" alt="Image: Things needed to construct the edge finder" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Roy sagte &amp;ldquo;Na ja, das Einzige, was du dafür brauchst, ist ein Kugellager. Befestige es auf einer Achse und wechsle sie in die Spindel ein wie jedes andere Werkzeug auch. Lasse die Spindel im niedrigen Drehzahlbereich laufen und bewege die Maschine langsam in Richtung Werkstückkante. Sobald der äußere Ring des Kugellagers nicht mehr mitläuft, hast du die Kante gefunden.&amp;rdquo; Alles was ich dann noch tun muss, ist, den Radius des Kugellagers auf die Maschinenposition zu addieren und ich habe die Position bestimmt.&lt;/p&gt;&#10;&lt;h3 id="aufbau"&gt;Aufbau&lt;/h3&gt;&#10;&lt;p&gt;Ich fand die Idee genial. Da ich leider kein gut sortiertes Metallfachgeschäft in der Nähe habe, durchwühlte ich den Haushalt nach einem Kugellager. Voilà, meine Kinder haben einen Fidget Spinner, den sie eine Weile nicht mehr benutzt haben. Also habe ich ihn&amp;hellip; stibitzt. Er enthält ein Kugellager mit 22mm Durchmesser für eine 8mm Welle.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-05-21_edgefinder.jpg" alt="Image: The mounted edge finder" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Hierfür bot sich der Schaft eines stumpfen 8mm Bohrers an. Also schnitt ich ihn in der Mitte durch, setzte den Schaft in das Kugellager ein und befestigte ihn mit einem Tropfen Kleber - Fertig!&lt;/p&gt;&#10;&lt;p&gt;Update: Kurz nach Veröffentlichung dieses Artikels schrieb ein Leser, dass alte Inline Skates oder Roller auch hervorragende Spender für Kugellager im Haushalt seien.&lt;/p&gt;&#10;&lt;h2 id="kanten-finden"&gt;Kanten finden&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Wechseln Sie den selbstgebauten Kantenfinder in die Spindel ein und verfahren sie in die Nähe des Werkstücks&lt;/li&gt;&#10;&lt;li&gt;Schalten Sie die Spindel bei niedriger Drehzahl ein, z.B. &lt;code&gt;S500....S3000&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Optional: Für eine einfachere Positionsbestimmung wechseln Sie in das Maschinenkoordinatensystem&lt;/li&gt;&#10;&lt;li&gt;Lassen Sie die Maschine langsam in Richtung des Werkstücks fahren, z.B. mit dem Kommando &lt;code&gt;G01 X&amp;lt;endpunktImWerkstück&amp;gt; F1&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Drücken Sie auf &lt;code&gt;Stop/Pause&lt;/code&gt;, sobald das Lager sich nicht mehr mitdreht&lt;/li&gt;&#10;&lt;li&gt;Notieren Sie sich die Maschinenkoordinaten bzw. verschieben das Arbeitskoordinatensystem der angetasteten Achse &lt;code&gt;G92&lt;/code&gt; auf diesen Punkt&lt;/li&gt;&#10;&lt;li&gt;Wiederholen Sie ab Schritt 2 für die nächste Achse&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2023-05-22_edgefinder_action.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Einfacher Kantenfinder im Einsatz.&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Hardware Test</title><link>https://blog.schallbert.de/testing-hardware/</link><pubDate>Sun, 23 Apr 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/testing-hardware/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-04-23_subsystem_test-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Subsystem boundary diagram"&#10; title="Hardware Test" /&gt;&#10;&lt;h2 id="ein-neuer-antrieb"&gt;Ein neuer Antrieb&lt;/h2&gt;&#10;&lt;p&gt;Kürzlich habe ich Zerspanobert ein &lt;a href="https://blog.schallbert.de/projects/spindle-upgrade/"&gt;Spindel-Upgrade&lt;/a&gt; gegönnt. Ich bin jetzt auf eine Drehstrom-Asynchronmaschine umgestiegen, welche mit einem 24V-Lüfter fremdgekühlt wird. Für den Betrieb im benötigten Drehzahlbereich musste ich in einen Frequenzumrichter investieren, der in meinem Falle wie beim vorigen Fräsmotor auch über ein analoges &lt;code&gt;0-10V&lt;/code&gt; Signal gesteuert wird. Aus Sicht der CNC ist die neue Spindel bis auf eine Ausnahme identisch mit der alten: Der Frequenzumrichter kann nun ein &amp;ldquo;Not-Aus&amp;rdquo; Signal erzeugen und an die Steuerung melden, z.B. wenn die Drehzahl ungeplant abfällt, zu lange ein Überlastzustand auftritt etc.&lt;/p&gt;&#10;&lt;p&gt;Wie man sich vorstellen kann ist das neue Subsystem rund um die Spindel deutlich komplizierter als das alte.&lt;/p&gt;&#10;&lt;h2 id="warum-extra-tests"&gt;Warum extra Tests?&lt;/h2&gt;&#10;&lt;p&gt;Mit höherer Systemkomplexität erhöht sich auch das Risiko für Fehlfunktionen. Es sind eine Menge mehr elektrischer, elektronischer und mechanischer Bauteile verbaut als bei meinem Fräsmotor, die selbst Defekte aufweisen oder bei falscher Verdrahtung selbst in Rauch aufgehen können. Da ich als Systemdesigner selbst nur ein Mensch bin der Fehler macht, wäre es nicht besonders schlau, das System irgendwie zusammenzustecken und dann direkt auszuprobieren.&lt;/p&gt;&#10;&lt;p&gt;Während ich die Verdrahtung erstellte, überlegte ich mir also, wie ich das System effizient testen könne, ohne etwas zu zerstören.&lt;/p&gt;&#10;&lt;h2 id="teststrategie"&gt;Teststrategie&lt;/h2&gt;&#10;&lt;p&gt;Ich habe die unterschiedlichen Testbereiche von meiner Arbeit in der Softwareindustrie geborgt. Sie passen hier denke ich recht gut hinein:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Unit&lt;/strong&gt;: Ein Test, der das Verhalten einer Komponente des Systems isoliert überprüft. Alle externen Einflüsse wurden entweder entfernt oder durch Komponenten ersetzt, auf die im Test selbst problemlos zugegriffen werden kann. Beispiel: Prüfe, ob der Lüfter bei Anlegen seiner Betriebsspannung wie erwartet im richtigen Drehsinn anläuft.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Integration&lt;/strong&gt;: Diese Tests prüfen das Zusammenspiel verschiedener Komponenten, welche gemeinsam eine Funktion abbilden. Beispiel: Überprüfe, ob die Luftkühlung der Spindel auch nach Abschalten derselben für eine bestimmte Zeit nachläuft.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Subsystem&lt;/strong&gt;: Verifiziert die korrekte Interaktion aller Komponenten eines Subsystems. Beispiel: Die Spindeldrehzahl kann über eine analoge Spannung am Eingang des Frequenzumrichters verändert werden.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;System (End2End)&lt;/strong&gt;: Diese Tests werden aus Kundensicht ausgeführt und durchlaufen das komplette System. Beispiel: Wenn ich den Not-Aus an der CNC betätige, muss der Frequenzumrichter die Spindel umgehend sicher zum Stillstand bringen.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-04-23_test_scopes.jpg" alt="Image: different test scopes of my CNC system" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="teste-früh"&gt;Teste früh&lt;/h3&gt;&#10;&lt;p&gt;Aus meiner Erfahrung macht es Sinn, die Tests zum frühestmöglichen Zeitpunkt durchzuführen. Nachdem die Spindel geliefert worden war, konnte ich auf diese Weise sofort nachvollziehen, ob alle Lager leichtgängig sind, ob der Lüfter funktioniert und ob der elektrische Widerstand der Statorwicklungen im erwarteten Bereich liegt. Alle diese Tests waren durchführbar noch bevor ich mit dem Bau des Schaltschranks überhaupt begonnen hatte.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2023-04-23-blowertest.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Unit test: Lüfter&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die schnell und einfach durchführbaren Unit Tests können einen Haufen Zeit sparen, denn diese Fehlerquellen kann man später bei der Inbetriebnahme direkt ausschließen. Und findet man doch etwas so früh, kann man sich direkt um Ersatz kümmern und hält nicht den Einrichtbetrieb auf.&lt;/p&gt;&#10;&lt;h3 id="teste-von-unten-nach-oben"&gt;Teste &amp;ldquo;von unten nach oben&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-04-23_test_pyramid.jpg" alt="Image: my test pyramid" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Bei den vier Testebenen versuche ich, so viel wie möglich mit einfachen und schnellen Unit Tests abzudecken. Was hier noch nicht geht, wird jeweils auf der nächsthöheren Ebene abgearbeitet und so weiter. Am Ende möchte ich nur ein paar Wenige Tests aus Kundensicht durchführen, da diese meist einen größeren Aufwand für Vor- und Nachbereitung benötigen und außerdem zumeist nicht genau in der Lage sind, den Fehler zu lokalisieren.&lt;/p&gt;&#10;&lt;p&gt;Dies deckt sich mit dem Konzept der &lt;a href="https://martinfowler.com/testing/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Testpyramide&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, welche in der Softwareentwicklung des Öfteren herangezogen wird.&lt;/p&gt;&#10;&lt;h2 id="test-plan"&gt;Testplan&lt;/h2&gt;&#10;&lt;p&gt;Für meine privaten Hobbyprojekte schreibe ich üblicherweise keinen Testplan. Meine Systeme sind meist deutlich einfacher als dieses. Außerdem benötigen nur die wenigsten potentiell gefährliche Niederspannung wie in diesem Fall.&lt;/p&gt;&#10;&lt;p&gt;Um das System besser zu visualisieren habe ich mal einen groben Schaltplan mit allen Komponenten sowie relevanten Ein- und Ausgängen gezeichnet.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-04-23_system_layout.jpg" alt="Image: motor spindle subsystem layout" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Von diesem Bild habe ich einen Testplan abgeleitet. Hierbei habe ich versucht, die Interaktionen jeder Komponente mit jeder anderen abzudecken und wenn möglich versucht, dem Komponententest den Vorrang zu geben.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-04-23_test_plan.jpg" alt="Image: A test plan for the motor spindle subsystem" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2023-04-23-powersupplytest.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Unit test: Stromversorgung &amp;#43;24V OK&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Hier überprüfe ich die Versorgungsspannung für den 24V-Lüfter&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2023-04-23-relaytest.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Unit test: Abfallverzögerung&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Mit diesem Test stelle ich sicher, dass die Konfiguration der Anzugs- und Abfallverzögerung wie gewünscht funktioniert. Für den Test habe ich kürzere Zeiten gewählt als später im produktiven Betrieb.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2023-04-23-vfdtest.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Integrationstest: FU Inbetriebnahme&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Hier prüfe ich, ob der Frequenzumrichter die Spindel mit der korrekten Drehrichtung und mit unterschiedlichen Drehzahlen betreiben kann.&lt;/p&gt;&#10;&lt;h2 id="ausführung-der-tests"&gt;Ausführung der Tests&lt;/h2&gt;&#10;&lt;p&gt;Es gibt natürlich noch viele weitere hardwarebasierte Tests die ich oben nicht erwähnt habe. Dinge wie &amp;ldquo;ist die Spindel korrekt mit der Z-Achse ausgerichtet?&amp;rdquo; &amp;ldquo;Habe ich alle Schrauben mit dem vorgeschriebenen Drehmoment angezogen?&amp;rdquo; &amp;ldquo;Sind alle Stecker angeschlossen und verriegelt?&amp;rdquo;&lt;/p&gt;&#10;&lt;p&gt;Bevor ich nach dem Umbau also das erste mal wieder Späne mache, habe ich den Testplan komplett abgearbeitet und tatsächlich ein Problem auf Systemebene entdeckt: Ich habe den Analogausgang zur Vorgabe der Spindeldrehzahl falsch verdrahtet, sodass andere Pins das Signal führten als geplant. Dies führte dazu, dass sich die Spindel trotz Freigabe nicht starten ließ, obwohl die CNC &amp;ldquo;Spindel läuft&amp;rdquo; ausgab.&lt;/p&gt;&#10;&lt;p&gt;Dank der vorausgehenden Tests am Frequenzumrichter (&amp;ldquo;Reacts to speed command&amp;rdquo;) habe ich nur Minuten benötigt um den Fehler zu finden und zu beheben - denn ich wusste, am FU liegt&amp;rsquo;s nicht.&lt;/p&gt;&#10;&lt;h2 id="dokumente"&gt;Dokumente&lt;/h2&gt;&#10;&lt;p&gt;Falls Sie planen, eine Frequenzumrichtergesteuerte Motorspindel an Ihrer Maschine einzusetzen, können Sie sich meinen Testplan kostenlos herunterladen.&#10;In case you plan on adding a VFD-controlled spindle motor to your machine, please find my full test plan below.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/assets/docs/SpindleTests.pdf"&gt;VFD-controlled spindle motor test plan&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Aber bitte betrachten Sie diesen Plan mehr als Unterstützung denn als vollumfängliche Prüfung und beachten Sie vor allem die üblichen Warnhinweise:&lt;/p&gt;&#10;&lt;p&gt;⚠️ &lt;strong&gt;Warnung vor gefährlicher Spannung&lt;/strong&gt; ⚠️&lt;/p&gt;&#10;&lt;p&gt;Dieses System sollte nur von Profis konstruiert, zusammengebaut, angepasst, in Betrieb genommen und schließlich fachgerecht geprüft werden. In diesem Falle meine ich mit &amp;ldquo;Profi&amp;rdquo; jemanden, der eine abgeschlossene Berufsausbildung zum Elektriker oder Elektroniker, optimalerweise in der Fachrichtung Anlagenbau/Betriebstechnik sowie Erfahrung im Umgang mit Industrieelektronik vorzuweisen hat. Vermeiden Sie insbesondere, unter Spannung stehende Teile zu berühren oder an einer der Komponenten zu arbeiten, bevor der Frequenzumrichter nach dem Abschalten genügend Zeit hatte, seine Pufferkondensatoren zu entladen.&lt;/p&gt;&#10;</description></item><item><title>Dibond Tests</title><link>https://blog.schallbert.de/cut-dibond-tests/</link><pubDate>Fri, 10 Mar 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cut-dibond-tests/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_dibond_test-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Dibond engrave test runs"&#10; title="Dibond Tests" /&gt;&#10;&lt;p&gt;Anmerkung: Die in diesem Artikel analysierten Gravuren sind recht fein. Die Schrifthöhe beträgt zwischen 3 und 6mm, und die Größe des Werkstückes liegt bei 80x80mm. Blickt man von einer Distanz von 30cm oder mehr auf das Werkstück, so sind die rauen Kanten etc. nicht mehr wahrnehmbar.&lt;/p&gt;&#10;&lt;h2 id="weitere-qualitätsverbesserungen-bei-arbeiten-mit-dibond"&gt;Weitere Qualitätsverbesserungen bei Arbeiten mit Dibond&lt;/h2&gt;&#10;&lt;p&gt;Die in meinem &lt;a href="https://blog.schallbert.de/cut-dibond/"&gt;letzten Fräsprojekt&lt;/a&gt; in Dibond erzielten, nur mittelmäßigen Ergebnisse wollte ich so nicht auf mir sitzen lassen. Deshalb habe ich mögliche Einflussgrößen ersonnen und niedergeschrieben.&lt;/p&gt;&#10;&lt;p&gt;In diesem Artikel versuche ich, sie Stück für Stück zu analysieren und schließlich, falls erforderlich, auszuschalten.&lt;/p&gt;&#10;&lt;h3 id="mögliche-gründe-für-schlechte-qualität"&gt;Mögliche Gründe für schlechte Qualität&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cut-dibond-tests/#cutter-inspection"&gt;Verschlissene Fräser&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cut-dibond-tests/#cutter-geometry"&gt;Falsche Schneidengeometrie&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cut-dibond-tests/#speed-feed"&gt;Ungeeignete Parameter&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cut-dibond-tests/#milling-style"&gt;Gleichlauf statt Gegenlauf&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cut-dibond-tests/#engrave-depth"&gt;Probleme mit der Gravurtiefe&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cut-dibond-tests/#machine-vibrations"&gt;Maschinenvibrationen&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cut-dibond-tests/#spindle-mount"&gt;Fräsmotorhalterung zu weich&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cut-dibond-tests/#spindle-imbalance"&gt;Unwucht&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="cutter-inspection"&gt;Untersuchung der Fräser&lt;/h3&gt;&#10;&lt;p&gt;Ich bin kein Fräserexperte, aber ich kann mir kaum vorstellen, dass ein VHM-Fräser nach nur dreißig Arbeitsminuten in Kontakt mit &lt;code&gt;0.3mm&lt;/code&gt; starkem &lt;code&gt;AlMg1&lt;/code&gt; stumpf sein soll (Na gut, da mögen ein paar Zusätze drin sein für die gebürstete Oberfläche samt Färbung). Die Schneiden sehen recht sauber aus. Das Einzige, was mir auffällt, ist eine dünne Schicht Aluminium, welche der Schneide anzuhaften scheint.&lt;/p&gt;&#10;&lt;p&gt;Diese habe ich vorsichtig mit einem Cuttermesser entfernt. Nun sehen sie wieder normal aus.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Update Okt-23&lt;/em&gt;: Ich habe mit denselben Graviersticheln - Sie sind inzwischen ca. 4h gelaufen - etwas in Acrylglas (PMMA) gefertigt. Die Schnittkanten sind hier rauer als bei brandneuen Gravierfräsern.&lt;/p&gt;&#10;&lt;h3 id="cutter-geometry"&gt;Schneidengeometrie&lt;/h3&gt;&#10;&lt;p&gt;Die für diesen Test verwendeten Fräser haben keine Spiralnut zur Spanabfuhr, sondern sind auf der Eingriffseite offen ausgeführt. Daher ist aber auch stets die komplette Schneide im Eingriff und ich frage mich, ob das einen Unterschied macht. Vielleicht wäre eine gewundene Schneide (Habichtschnabel, Helix) hier besser geeignet?&lt;/p&gt;&#10;&lt;p&gt;Hintergrund dieser These ist, dass mein CAM-Werkzeug für Gravuren keine Pendelbewegung oder Helix zum Eintauchen verwendet sondern stets gerade in das Material eintaucht. Wenn man bei den Bildern unten (z.B. in der Vergrößerung des &lt;code&gt;t&lt;/code&gt;) genau hinsieht, so erkennt man Kratzer im Klarlack, wo der Fräser ins Material eingetaucht ist und die Späne nicht hat rechtzeitig abführen können.&lt;/p&gt;&#10;&lt;p&gt;Auf der anderen Seite werden spiralgenutete Fräser das Material nach oben ziehen. Da das Aluminiumblech lediglich thermisch mit dem PE-Kern verklebt ist, könnte die Haltekraft besonders bei filigranen Gravuren durchaus zu gering sein und sich die Aluminiumschicht ablösen - die Gefahr ist besonders hoch bei Inseln, wie es sie z.B. bei den Buchstaben &lt;code&gt;e, a, o, g, p...&lt;/code&gt; gibt.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Kurzgesagt&lt;/em&gt; Gravierfräser mit Spiralnut kaufen, ausprobieren und die Fräsergebnisse hier dokumentieren&lt;/p&gt;&#10;&lt;h3 id="speed-feed"&gt;Ungeeignete Parameter für Vorschub und Drehzahl&lt;/h3&gt;&#10;&lt;p&gt;Ich habe die empfohlenen Parameter nochmal geprüft, mit dem Hersteller der Fräser besprochen und sie auch in ein paar Berechnungstools eingegeben. Optimalerweise werden sie bei noch höherer Drehzahl als &lt;code&gt;24000U/min&lt;/code&gt; betrieben, was an meiner Maschine allerdings das Maximum ist - also behalte ich das so bei.&lt;/p&gt;&#10;&lt;p&gt;Also bleibt mir jetzt noch der Vorschub zum Herumspielen übrig. Schnell eine Testgravur im CAD erstellt und im CAM dann mit verschiedenen Vorschüben abfahren lassen: &lt;code&gt;F600mm/min&lt;/code&gt;, &lt;code&gt;F800mm/min&lt;/code&gt; sowie &lt;code&gt;F1000mm/min&lt;/code&gt;.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_workpiece.jpg" alt="Image: My Test workpiece with tryout engravings" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;In diesem Bild kann man viele Dinge erkennen:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Die Beschriftungen für verschiedene Vorschubgeschwindigkeiten sind so klein geraten, dass der Gravierstichel nicht einmal die Aluminiumschicht vollständig durchbricht. Vielmehr quetscht er das Aluminium in die darunter liegende Polyethylenschicht und zur Seite weg.&lt;/li&gt;&#10;&lt;li&gt;Obwohl ich die Vorschubgeschwindigkeit um bis zu 20% pro Umdrehung verändere, bleibt die Schnittqualität gleich. Dies bestätigt, dass für meinen Stichel in Dibond ein Vorschub von &lt;code&gt;F1000mm/min&lt;/code&gt; in Ordnung ist.&lt;/li&gt;&#10;&lt;li&gt;Es ist deutlich zu sehen, dass die äußeren Kanten der Gravur rau sind. Ich kann dies fühlen, wenn ich mit dem Finger über das Werkstück streiche.&lt;/li&gt;&#10;&lt;li&gt;Die inneren Ränder der Gravuren sind wirklich glatt. Sie glänzen im Gegensatz zu ihren matten Gegenstücken.&lt;/li&gt;&#10;&lt;li&gt;Die schwarzen, inneren Teile der Spirale sind sehr gleichmäßig und ohne Schnittspuren gefertigt.&lt;/li&gt;&#10;&lt;li&gt;Das große &lt;code&gt;T&lt;/code&gt; des Wortes &amp;ldquo;Test&amp;rdquo; sieht etwas seltsam aus, da senkrechte Kerben an der Schnittkante auftreten. Darauf kommen wir später noch zu sprechen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;&lt;em&gt;Kurzgesagt&lt;/em&gt;: Drehzahl- sowie Vorschubparameter sind OK und scheinen für mein Problem nicht die Ursache gewesen zu sein.&lt;/p&gt;&#10;&lt;h3 id="milling-style"&gt;Gleichlauf- vs. Gegenlauffräsen&lt;/h3&gt;&#10;&lt;p&gt;Um zu entscheiden, ob es bei der Verarbeitung von Aluminiumsandwichplatten eine bevorzugte &lt;a href="https://blog.schallbert.de/portal-milling/#milling-style"&gt;Fräsrichtung&lt;/a&gt; gibt, schauen wir uns die Spirale etwas genauer an:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_spiral.jpg" alt="Image: Detail view of the spiral, cut with the engrave bit" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die schmalere Linie wurde in einem Schritt gefertigt während die breitere einen zweiten Durchlauf mit nur &lt;code&gt;Z-0.1mm&lt;/code&gt; Tiefenzustellung hat.&#10;Sofort ist zu erkennen, dass die Gravur außen viel glattere Kanten hat als innen. Die Spirale wurde von außen nach innen mit einer rechtsdrehenden Spindel gefräst.&#10;Das bedeutet also, dass außen im Gegenlauf und innen im Gleichlauf gearbeitet wurde.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Kurzgesagt&lt;/em&gt; wird auf einer CNC zwar eher der Gleichlauf empfohlen, bei der Bearbeitung von Dibond ist unter Verwendung von Graviersticheln jedoch im Gegenlauf mit deutlich besseren Ergebnissen zu rechnen.&lt;/p&gt;&#10;&lt;h3 id="engrave-depth"&gt;Zu niedrige Gravurtiefe&lt;/h3&gt;&#10;&lt;p&gt;Wie die Bilder oben zeigen, sollte die Aluminiumschicht beim Gravieren satt durchbrochen werden. Nach ein paar weiteren Tests konnte ich die minimale Gravurtiefe bei &lt;code&gt;0.3mm&lt;/code&gt; Schichtstärke des Blechs bei etwa &lt;code&gt;0.5mm&lt;/code&gt; ermitteln.&lt;/p&gt;&#10;&lt;h3 id="zu-hohe-gravurtiefe"&gt;Zu hohe Gravurtiefe&lt;/h3&gt;&#10;&lt;p&gt;Diese Mutmaßung ergab sich bei meinem &lt;a href="https://blog.schallbert.de/cut-dibond/#quality-analysis"&gt;vorigen Experiment mit Dibond&lt;/a&gt;. Zum Vergleich habe ich hier zwei verschieden Dicke Gravurpfade gefertigt. Tiefere Gravuren werden tatsächlich etwas sauberer, wenn man sie mit einem Schlichtgang nochmal nachzieht. Für schmale Gravuren halte ich die Verbesserung für vernachlässigbar. Daher ist eine Hohe Gravurtiefe meiner Einschätzung nach kein Problem.&lt;/p&gt;&#10;&lt;h3 id="machine-vibrations"&gt;Vibrationen&lt;/h3&gt;&#10;&lt;p&gt;Betrachtet man erneut die Kerben und Unregelmäßigkeiten an der Schnittkante, so liegt der Schluss nahe, dass sie sich aus der gebürsteten Oberfläche ergeben. Tatsächlich nämlich wird die Oberfläche aber nicht gebürstet, sondern unter hohem Druck mit Hilfe von Walzen geprägt. Die Kerben könnten also durch Dichteunterschiede im Material begünstigt werden. Es gibt aber eine weitere mögliche Ursache: Maschinenvibrationen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_text.jpg" alt="Image: Test texts with different feed rates." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die Texte wurden mit verschiedenen Vorschubwerten gefertigt. Die Zahlen geben hierbei den Vorschub an: &lt;code&gt;6&lt;/code&gt; bedeutet &lt;code&gt;600mm/min&lt;/code&gt; und so weiter. Ich habe noch ein weiteres Bild gemacht, was die Buchstaben nochmal stärker vergrößert darstellt. Hier zeigt sich, dass die Texte bei größerem Vorschub ein klein wenig besser aussehen.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_ts.jpg" alt="Image: Macro zoom into the `t`&amp;#39;s of the text." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Im letzten halben Jahr habe ich viel mit den mechanischen Werten der Maschine experimentiert, um die Grenzen auszuloten. Ich habe damals recht hohe Werte für Geschwindigkeit und Beschleunigung festgelegt, da ich die Maschine für fähig hielt, diese zu unterstützen. &amp;ldquo;Unüblich hoch&amp;rdquo; nannte der Hersteller der Steuersoftware diese Werte, als ich ihn wegen eines anderen Problems kontaktierte (&lt;a href="https://blog.schallbert.de/projects/one-year-zerspanobert/#velocityerror"&gt;&amp;ldquo;Velocity was higher than max!&amp;rdquo;&lt;/a&gt;).&lt;/p&gt;&#10;&lt;p&gt;Also habe ich ein weiteres Teststück designt, wo ich nur gerade Linien und Texte gravieren würde, sodass rechtwinklige Ecken entstehen an denen die Maschine auf null abbremsen und von dort wieder beschleunigen würde.&lt;/p&gt;&#10;&lt;p&gt;Dieses mal habe ich den Vorschub bei &lt;code&gt;F1000mm/min&lt;/code&gt; belassen, aber in jedem Durchgang die Beschleunigungswerte der Maschine verändert.&lt;/p&gt;&#10;&lt;p&gt;Dieses Experiment zeigt, dass die vertikalen Kerben bei geringerer Beschleunigung deutlich weniger sichtbar werden und unterhalb irgendwann sogar ganz verschwinden. Da ich die Maschine für diese Anpassung stets neustarten musste, waren Werkzeug und Z0-Höhe immer neu einzumessen. Möglicherweise hatte ich ein Staubkorn unter dem Werkzeuglängensensor bei der Vermessung für die geringste Beschleunigung, daher kommt die Gravur hier nicht ganz so gut hervor.&lt;/p&gt;&#10;&lt;section class="hugo-gallery"&gt;&#10; &lt;div class="hugo-gallery__frame"&gt;&#10; &lt;div class="hugo-gallery__grid" role="list"&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-0" aria-label="Image: Front view of used V-cut bit. "&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_vcut_front.jpg" alt="Image: Front view of used V-cut bit. " class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-1" aria-label="Image: 45° Front view of used V-cut bit. It carries some residual aluminium material."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_vcut_front45.jpg" alt="Image: 45° Front view of used V-cut bit. It carries some residual aluminium material." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-2" aria-label="Image: Side view of used V-cut bit. The cutting edge looks sharp and polished."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_vcut_side.jpg" alt="Image: Side view of used V-cut bit. The cutting edge looks sharp and polished." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-3" aria-label="Image: Rear view of used V-cut bit. At the very tip, it is more shiny than at the rest of its body."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_vcut_back.jpg" alt="Image: Rear view of used V-cut bit. At the very tip, it is more shiny than at the rest of its body." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-4" aria-label="Image: Test text and numbers. At 800mm/s² acceleration, there are visible horizontal marks in the path and the quality of cut is poor."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_test800.jpg" alt="Image: Test text and numbers. At 800mm/s² acceleration, there are visible horizontal marks in the path and the quality of cut is poor." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-5" aria-label="Image: Test text and numbers. At 400mm/s² the highly detailed text does not look too good. There are chatter marks everywhere."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_test400.jpg" alt="Image: Test text and numbers. At 400mm/s² the highly detailed text does not look too good. There are chatter marks everywhere." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-6" aria-label="Image: Test text and numbers. Even with lowest acceleration, the problem is to stay. The aluminium layer tends to squash into the much softer polyurethane core material, creating mediocre results."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_test200.jpg" alt="Image: Test text and numbers. Even with lowest acceleration, the problem is to stay. The aluminium layer tends to squash into the much softer polyurethane core material, creating mediocre results." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;/div&gt;&lt;div class="hugo-gallery__caption"&gt;Testergebnisse: Detailaufnahmen von Stichel und Material bei variabler Maschinenbeschleunigung.&lt;/div&gt;&lt;/div&gt;&#10;&lt;/section&gt;&#10;&#10; &lt;div class="hugo-gallery__full" aria-live="polite"&gt;&lt;figure id="gallery-full-0" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_vcut_front.jpg" alt="Image: Front view of used V-cut bit. " class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;The 6mm 90° 0.5m tip V-cut tool. At the cutting edge, there&amp;#39;s a little built-up material.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-1" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_vcut_front45.jpg" alt="Image: 45° Front view of used V-cut bit. It carries some residual aluminium material." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;It has been used for roughly 30minutes. The cutting edge looks intact.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-2" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_vcut_side.jpg" alt="Image: Side view of used V-cut bit. The cutting edge looks sharp and polished." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Minimal residues of Aluminium seem to stick to its tip.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-3" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_vcut_back.jpg" alt="Image: Rear view of used V-cut bit. At the very tip, it is more shiny than at the rest of its body." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Also here, the tip colour is different than the rest of the bit.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-4" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_test800.jpg" alt="Image: Test text and numbers. At 800mm/s² acceleration, there are visible horizontal marks in the path and the quality of cut is poor." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Result with high machine acceleration&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-5" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_test400.jpg" alt="Image: Test text and numbers. At 400mm/s² the highly detailed text does not look too good. There are chatter marks everywhere." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Result with medium machine acceleration&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-6" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_test200.jpg" alt="Image: Test text and numbers. Even with lowest acceleration, the problem is to stay. The aluminium layer tends to squash into the much softer polyurethane core material, creating mediocre results." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Result with low machine acceleration.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;/section&gt;&#10;&lt;p&gt;Beleuchten wir kurz, ob die Maschine bei diesen kleinen Strukturen überhaupt die Zielgeschwindigkeit von &lt;code&gt;F1000mm/min&lt;/code&gt; erreichen kann: Die Texthöhe beträgt &lt;code&gt;s0=6mm&lt;/code&gt;, sodass die Maschine bei &lt;code&gt;a=800mm/s²&lt;/code&gt;, etwa &lt;code&gt;t=F/a=20ms&lt;/code&gt; für die Beschleunigung auf den angepeilten Vorschub benötigt. Dabei legt sie eine Distanz von &lt;code&gt;s=a/2*t^2=0.17mm&lt;/code&gt; zurück. Daher wird für &lt;code&gt;s1=6-2*0.17=5.66mm&lt;/code&gt; mit voller Geschwindigkeit verfahren, z.B. für den vertikalen Strich im Buchstaben &lt;code&gt;t&lt;/code&gt;. Hier sind die kinematischen Gleichungen mal wieder sehr nützlich gewesen 😃&lt;/p&gt;&#10;&lt;p&gt;Jetzt wäre es noch interessant, die Eigenfrequenz der Maschine in X-Richtung zu bestimmen, denn hier treten die Kerben ja auf. Meine Überschlagsrechnung ergibt: &lt;code&gt;11&lt;/code&gt; Kerben auf &lt;code&gt;6mm&lt;/code&gt; Weg bei einer Geschwindigkeit von &lt;code&gt;F1000mm/min --&amp;gt; F16.67mm/s&lt;/code&gt; ergibt &lt;code&gt;fE=30Hz&lt;/code&gt;. Da die Anregung der Maschine in der &amp;ldquo;Ansteigenden&amp;rdquo; Halbwelle geschieht, sollte ich stets versuchen, die Maschine weit weg von &lt;code&gt;t=1/(2f) = 16ms&lt;/code&gt; auf Zielgeschwindigkeit zu bringen und so die Resonanzfrequenz vermeiden.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Kurzgesagt&lt;/em&gt;: Die kinematischen Parameter sollten - besonders bei weniger steifen und eher leichten Maschinen - fein auf die jeweilige Fräsmaschine angepasst werden. Hierbei it es hilfreich, die Eigenfrequenz der instabilsten Achse zu kennen und &amp;ldquo;weiträumig&amp;rdquo; zu umschiffen. Für meine Maschine habe ich festgestellt, dass eine Beschleunigung von &lt;code&gt;a=400mm/s²&lt;/code&gt; noch ausreichend zügig ist, meine Probleme mit am Werkstück sichtbaren Schwingungen aber löst.&lt;/p&gt;&#10;&lt;h3 id="spindle-mount"&gt;Halterung des Fräsmotors&lt;/h3&gt;&#10;&lt;p&gt;Ich wusste, dass dies bei &lt;a href="https://blog.schallbert.de/cnc-router-overload/"&gt;Überlastung der Maschine&lt;/a&gt; ein Problem darstellen würde. Für Gravurarbeiten jedoch fräst die Spindel quasi im Leerlauf. Daher klammere ich diesen Aspekt hier aus.&lt;/p&gt;&#10;&lt;h3 id="spindle-imbalance"&gt;Unwucht des Fräsers oder der Spannzange&lt;/h3&gt;&#10;&lt;p&gt;Wenn dies der Fall gewesen wäre, hätte ich ungewöhnliche Geräusche oder Vibrationen der Maschine wahrnehmen müssen. Da dies alles nicht vorlag, schließe ich Unwucht des Werkzeugs als Ursache aus.&lt;/p&gt;&#10;&lt;h2 id="maßnahmen"&gt;Maßnahmen&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Fräser nach Benutzung prüfen und reinigen, bei übermäßigem Verschleiß austauschen.&lt;/li&gt;&#10;&lt;li&gt;Vermeiden von Gleichlauf bei der Verarbeitung von Dibond. Die Gravurpfade lieber minimal weiter auslegen und dann beide Seiten der Gravur im Gegenlauf fertigen.&lt;/li&gt;&#10;&lt;li&gt;Bei höheren Gravurtiefen einen Schlichtgang hinzufügen&lt;/li&gt;&#10;&lt;li&gt;Maschinenbeschleunigung in Verbindung mit Vorschüben bei weniger steifen Maschinen drosseln.&lt;/li&gt;&#10;&lt;li&gt;Vor Einstieg in die Fertigung ein paar Probefräsungen durchführen.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-10_dibondmetallic.jpg" alt="Image: Dibond metallic, cut after all improvements were applied" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Dieses Teil wurde nach Ausführung der oben genannten Maßnahmen gefertigt.&lt;/p&gt;&#10;</description></item><item><title>Aluminiumverbundmaterial</title><link>https://blog.schallbert.de/cut-dibond/</link><pubDate>Thu, 02 Mar 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cut-dibond/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_btc_beermat-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Engraving Dibond, required manual rework"&#10; title="Aluminiumverbundmaterial" /&gt;&#10;&lt;h2 id="experimente-mit-dibond"&gt;Experimente mit Dibond&lt;/h2&gt;&#10;&lt;p&gt;Ein Freund von mir gründet gerade eine Unternehmung. Er wollte etwas haben, was er seinen Kunden geben kann, etwas, an das sie sich erinnern und so erneut seine Dienste in Anspruch nehmen würden. Es sollte nicht der Standard-Kugelschreiber oder die Tasse sein. Sondern etwas, das etwas näher an dem dran ist, was er selbst tut. Irgendwas metallisches. Vielleicht eine Visitenkarte aus Metall, mit dreidimsionalen Touch. Etwas Graviertes.&lt;/p&gt;&#10;&lt;p&gt;Als ich passende Materialien recherchierte, stieß ich auf Aluminiumverbundplatten. Dies sind Sandwichplatten, die einen Kern aus &lt;a href="https://en.wikipedia.org/wiki/Polyethylene" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Polyethylen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; besitzen, welcher auf beiden Seiten mit dünnen Aluminiumblechen kaschiert sind. Solche Platten sind bekannt unter den Markennamen Dibond®, Dilite®, Alcubond® etc. und werden blank oder beschichtet, lackiert oder sogar mit strukturierter Oberfläche angeboten.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;Der PE-Kern ist schwarz durchgefärbt, also könnte es sich prima für Gravurarbeiten eignen&amp;rdquo;, dachte ich.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Also habe ich ein paar Muster bestellt und sie meinem Freund gezeigt. Er war völlig aus dem Häuschen. Und so begann es.&lt;/p&gt;&#10;&lt;h3 id="der-untersetzer"&gt;Der Untersetzer&lt;/h3&gt;&#10;&lt;p&gt;Wir überlegten, was wir alles mit diesem Material anstellen könnten. Es sollte leicht zu designen sein und nicht zu groß werden, sodass es leicht als Werbegeschenk weitergegeben werden kann.&lt;/p&gt;&#10;&lt;p&gt;Ich wusste, dass Alu-Sandwichplatten zu dreidimensionalen Objekten gefaltet werden können (&lt;a href="https://www.youtube.com/watch?v=9h0RVwTpabk" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Youtube video link&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) - Aber das schien uns zu kompliziert für den Anfang. Daher war die nächste Idee:&lt;/p&gt;&#10;&lt;p&gt;Untersetzer gravieren. Rund oder quadratisch mit abgerundeten Ecken, sogar besondere Formen sind möglich.&lt;/p&gt;&#10;&lt;p&gt;Ich begann, ein paar meiner Gravurfräser für ein paar Experimente auf das Material loszulassen. Die Qualität sah OK aus, war aber noch lange nicht perfekt. Die Aluminiumschicht neigt dazu, zu verschmieren und möchte dem Fräser ausweichen, also drückt es sich beim Eintauchen (90°) in das weichere Polyethylen ein und benötigt so höhere Gravurtiefen als die Blechdicke allein vermuten lassen würde.&lt;/p&gt;&#10;&lt;p&gt;Der Freund von mir meinte jedoch, ich solle es einfach weiter probieren. Und so bestellte ich ein paar Platten und setzte die Experimente fort.&lt;/p&gt;&#10;&lt;h3 id="fräser-vorschübe-drehzahl"&gt;Fräser, Vorschübe, Drehzahl&lt;/h3&gt;&#10;&lt;p&gt;Da die Untersetzer filigrane Teile kleiner als 100x100mm sind, nutze ich kleine Fräser:&lt;/p&gt;&#10;&lt;p&gt;| VHM-Fräser | Schneiden | ⌀ | Drehzahl | Vorschub XY | Vorschub Z | Z+ |&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;[mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;[RPM]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;[mm/min]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;[mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;[mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Acrylfräser 30° upcut&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;24000&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1600&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;800&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Gravierfräser 90°&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;24000&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1000&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;500&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1.5&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Die Werte mögen für die Aluminiumbearbeitung zu aggressiv sein, ich muss aber einen Mindestvorschub einhalten um den PE-Kern des Plattenmaterials nicht aufzuschmelzen. Die Späne sehen jedenfalls gut aus - kurz und dick. Mit diesen Werten gibt Zerspanobert beim Fräsen eine ruhiges, summendes Geräusch von sich.&lt;/p&gt;&#10;&lt;h3 id="das-erste-teil"&gt;Das erste Teil&lt;/h3&gt;&#10;&lt;p&gt;Ich habe zuerst den Halter für die Untersetzer gefertigt. Er besteht aus vier Teilen, die einfach zusammengesteckt werden können. Sie müssen nicht geklebt werden, da ich eine Übergangspassung gewählt habe. Ich verwende einen Anschlag als Orientierung für den XY-Nullpunkt des Werkstücks, da ich beidseitig fräsen möchte. Auf diese Weise kann ich die Ausschnitte in einem letzten Schritt anbringen und den Vakuumdruck auf meinem Tisch bis zu diesem letzten Schritt aufrechterhalten. Außerdem wollte ich vermeiden, dass ich die Teile manuell nachbearbeiten muss.&lt;/p&gt;&#10;&lt;p&gt;Der erste Versuch war miserabel - die vorderen und hinteren Fräsbahnen waren nicht perfekt ausgerichtet. Schon ein paar Zehntelmillimeter Versatz zwischen Vorder- und Rückseite machen hier einen großen Unterschied, also musste ich die Ursache beheben (Kanten am Anschlag, die ich vorher nicht bemerkt hatte und die ich abschleifen musste) und es erneut versuchen.&lt;/p&gt;&#10;&lt;p&gt;Hier ist das Ergebnis:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_btc_holder.jpg" alt="Image: The coaster holder, made of Aluminium composite" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Ich bin ziemlich zufrieden damit. Ich musste keine manuelle Nacharbeit leisten - die Teile sind einfach so aus der CNC gefallen. Die Fasen allerdings, die ich hinzugefügt habe, um dem Material eine glatte Oberfläche zu geben, waren leider von sehr unterschiedlicher Qualität. Während sie auf der &amp;ldquo;Rückseite&amp;rdquo; wirklich gut geworden sind, fühlen sie sich auf der Vorderseite viel rauer an. Hier musste das Werkzeug aber auch tiefer eintauchen.&lt;/p&gt;&#10;&lt;h3 id="sechs-stück-auf-einmal"&gt;Sechs Stück auf einmal&lt;/h3&gt;&#10;&lt;p&gt;Dann wählte ich ein einfach herzustellendes Logo - das Bitcoin-Blitz-Symbol - und plante einige einfache Gravurpfade, um es zu gravieren. Auch hier habe ich zweiseitig gefräst, so dass das Material nicht vollständig durchtrennt wird. Unten bleibt eine hauchdünne Schicht Aluminium stehen, die in einem zweiten Durchgang auf der Unterseite beim Fase fräsen entfernt wird.&lt;/p&gt;&#10;&lt;p&gt;Die Maschine brauchte für diesen Job etwa &lt;code&gt;20min&lt;/code&gt;. Leider erwies sich der Anschlag als zu ungenau, um Vorder- und Rückseite mit demselben Nullpunkt passgenau zu fertigen. Der Ausschnitt an der Unterseite wich nur um ein oder zwei Zehntelmillimeter ab, was in meinem Fall die Gesamtabweichung verdoppelt, wenn ich ihn um seine diagonale Achse drehte. Auf diese Weise hatte ein Teil der Kontur meines Untersetzers eine sehr tiefe Fase, während die andere Hälfte überhaupt keine Fase aufwies.&lt;/p&gt;&#10;&lt;p&gt;Ich muss bald eine Lösung dafür finden. Bis dahin schleife ich die Kanten von Hand nach.&#10;Dann habe ich die restlichen Späne entlang der Gravurwege mit einer Zahnbürste entfernt und das obige Titelfoto aufgenommen.&lt;/p&gt;&#10;&lt;h2 id="quality-analysis"&gt;Qualitätsanalyse&lt;/h2&gt;&#10;&lt;p&gt;Nachdem ich das Teil genauer untersucht hatte, machte ich die folgenden Beobachtungen:&lt;/p&gt;&#10;&lt;section class="hugo-gallery"&gt;&#10; &lt;div class="hugo-gallery__frame"&gt;&#10; &lt;div class="hugo-gallery__grid" role="list"&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-0" aria-label="Image: BTC coasters on the machine, already fully cut."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_btc_cutouts.jpg" alt="Image: BTC coasters on the machine, already fully cut." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-1" aria-label="Image: A coaster holder: A U-shaped assembly holds up to six round coasters."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_holder_cutouts.jpg" alt="Image: A coaster holder: A U-shaped assembly holds up to six round coasters." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-2" aria-label="Image: Closeup of coaster&amp;#39;s bottom area. The edges look rough on deeper engravings. There is no uniform, shiny cut."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_engrave_closeup.jpg" alt="Image: Closeup of coaster&amp;#39;s bottom area. The edges look rough on deeper engravings. There is no uniform, shiny cut." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-3" aria-label="Image: Closeup of coaster&amp;#39;s lightning area. Its cutting edges look clean and shiny, the engraving is pretty shallow."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_engrave_closeup2.jpg" alt="Image: Closeup of coaster&amp;#39;s lightning area. Its cutting edges look clean and shiny, the engraving is pretty shallow." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-4" aria-label="Image: Closeup of coaster&amp;#39;s top area. Due to the slow feed, PU chips have molten to the cutting channel."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_engrave_closeup3.jpg" alt="Image: Closeup of coaster&amp;#39;s top area. Due to the slow feed, PU chips have molten to the cutting channel." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-5" aria-label="Image: Closeup of coaster&amp;#39;s lightning area, focusing on the deeper engrave. There are some marks on the aluminium edge."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_engrave_closeup4.jpg" alt="Image: Closeup of coaster&amp;#39;s lightning area, focusing on the deeper engrave. There are some marks on the aluminium edge." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;/div&gt;&lt;div class="hugo-gallery__caption"&gt;Prototypen eines Untersetzers aus Metall.&lt;/div&gt;&lt;/div&gt;&#10;&lt;/section&gt;&#10;&#10; &lt;div class="hugo-gallery__full" aria-live="polite"&gt;&lt;figure id="gallery-full-0" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_btc_cutouts.jpg" alt="Image: BTC coasters on the machine, already fully cut." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Top sheet machining complete. Cutout and engravings.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-1" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_holder_cutouts.jpg" alt="Image: A coaster holder: A U-shaped assembly holds up to six round coasters." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Coaster holder cutouts, bottom view&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-2" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_engrave_closeup.jpg" alt="Image: Closeup of coaster&amp;#39;s bottom area. The edges look rough on deeper engravings. There is no uniform, shiny cut." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Rough edges where engravings are more deep&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-3" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_engrave_closeup2.jpg" alt="Image: Closeup of coaster&amp;#39;s lightning area. Its cutting edges look clean and shiny, the engraving is pretty shallow." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Less wide engravings looks more clean.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-4" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_engrave_closeup3.jpg" alt="Image: Closeup of coaster&amp;#39;s top area. Due to the slow feed, PU chips have molten to the cutting channel." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Residues and chips left in the engrave channels&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-5" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-03-02_engrave_closeup4.jpg" alt="Image: Closeup of coaster&amp;#39;s lightning area, focusing on the deeper engrave. There are some marks on the aluminium edge." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Wondering why engrave quality reduces on deeper engravings&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;/section&gt;&#10;&lt;h3 id="alle-teile-sehen-gleich-aus"&gt;Alle Teile sehen gleich aus&lt;/h3&gt;&#10;&lt;p&gt;Das sind doch gute Neuigkeiten. Es bedeutet, dass die Fräser, die ich erstmals bei &lt;a href="https://www.vhf.com/de-de/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;vhf&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; gekauft habe, scharf geblieben sind und ihr Durchmesser daher konstant war. Ich kann mit den eingestellten Schnittweiten also wahrscheinlich viele weitere Prototypen fertigen. Dibond scheint also ein weniger anspruchsvolles Material zu sein als HPL, wo meine Fräser oft nach zwei Teilen bereits abgenutzt sind und z.B. Passungen klemmen - mehr dazu &lt;a href="https://blog.schallbert.de/cnc-router-overload/"&gt;in diesem Beitrag&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Es bedeutet auch, dass die Maschine keine Schrittverluste oder andere mechanische Probleme hat, und dass mein Klemmbereich eben ist, sodass der &lt;code&gt;90°&lt;/code&gt;-Gravierstichel sehr gleichmäßige Fräsergebnisse liefert. Linien behalten durch die zur Ausrichtung der Z-Achse orthogonale Ebene des Maschinentisches eine einheitliche Breite.&lt;/p&gt;&#10;&lt;h3 id="breitere-gravuren-sind-qualitativ-schlechter-als-schmalere"&gt;Breitere Gravuren sind qualitativ schlechter als schmalere&lt;/h3&gt;&#10;&lt;p&gt;Ich bin mir nicht sicher, warum. Da ich noch kein Experte im Umgang mit Dibond bin und keine Ausbildung als Zerspanungsmechaniker vorweisen kann, muss ich raten: Bekommt der Gravierstichel die Späne nicht rechtzeitig aus dem Fräskanal heraus gefördert? Dadurch würde er sie bei der nächsten Drehung erneut erfassen und &amp;ldquo;durch das Material ziehen&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Sollte dies der Fall sein, würde ein zusätzlicher Schlichtgang Abhilfe schaffen.&lt;/p&gt;&#10;&lt;h3 id="kantenbrechen-ohne-vorherige-nutfräsung-ergibt-rauere-schnittkanten"&gt;Kantenbrechen ohne vorherige Nutfräsung ergibt rauere Schnittkanten&lt;/h3&gt;&#10;&lt;p&gt;Hier könnte ich es mit demselben Problem wie oben zu tun haben. Ich probiere es mal mit einem zusätzlichen Schlichtgang aus und schaue, ob es dann besser wird. Eine Tiefgehendere Analyse hierzu gibt es &lt;a href="https://blog.schallbert.de/cut-dibond-tests/"&gt;in diesem Beitrag&lt;/a&gt;&#10;Eine weitere Alternative wäre, mit Vorschub und Drehzahl herumzuprobieren um das Optimum herauszufinden. Dafür benötige ich aber erst einmal frisches Material 😅&lt;/p&gt;&#10;&lt;h3 id="zweiseitige-bearbeitung-werkstückposition-präzise-festlegen"&gt;Zweiseitige Bearbeitung? Werkstückposition präzise festlegen!&lt;/h3&gt;&#10;&lt;p&gt;Offensichtlich war ich nicht in der Lage, die Position des Werkstückes nach dem Umdrehen auf die Rückseite mit Hilfe eines Anschlags genau genug festzulegen. Hier reichen ein paar Zehntelmillimeter Abweichung und die Sandwichplatte ist Schrott.&lt;/p&gt;&#10;&lt;p&gt;Lösungsansätze:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Einen &lt;code&gt;Job00&lt;/code&gt; einfügen, der Locatorlöcher (Positionsgeber) mit &lt;code&gt;5.04mm&lt;/code&gt; Durchmesser in einen Teil des Werkstücks einbringt, was später ein Abfallstück wird. Wenn ich sie symmetrisch platziere und diese Löcher ebenfalls im Frästisch fertige, so kann ich Passstifte einbringen, die mir die genaue Werkstückpositionierung beim Drehen auf die Rückseite sicherstellen.&lt;/li&gt;&#10;&lt;li&gt;Die Position des Anschlags überprüfen und sicherstellen, dass das Rohteil genau rechtwinklig und ohne Lichtspalt dort anliegt.&lt;/li&gt;&#10;&lt;li&gt;Ich könnte auch durchfräsen und die scharfen Kanten auf der Rückseite an einem Frästisch von Hand brechen. Da ich allerdings a) keine Frästisch besitze, b) meine Werkstücke oft sehr klein sind und c) Ich manuelle Nacharbeit verabscheue, wird dies wohl eine Rückfallebene bleiben für den Fall, dass die beiden obigen Lösungen für mich nicht funktionieren.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="einige-späne-kleben-in-der-fräsnut-fest"&gt;Einige Späne kleben in der Fräsnut fest&lt;/h3&gt;&#10;&lt;p&gt;Ich habe keine Ahnung, warum. Werden die Späne zu heiß und schmelzen sich in die Polyethylenschicht ein? Ich werde wohl mal mit dem Fräserhersteller telefonieren müssen, um das zu klären.&lt;/p&gt;&#10;&lt;h2 id="zusammenfassung"&gt;Zusammenfassung&lt;/h2&gt;&#10;&lt;p&gt;Alles in Allem kann ich einigermaßen zufrieden sein. Die Maschine ist in der Lage, Dibond und andere Aluminiumverbundmaterialien zu bearbeiten und die Ergebnisse sehen für einen ersten Versuch OK aus. Das Verbundmaterial nutzt die Fräser nicht übermäßig schnell ab und sieht einfach toll aus im Kontrast mit dem schwarzen Kern. Die Späne stören mich allerdings etwas, denn sie haften überall an und mögen ungern abgesaugt werden.&lt;/p&gt;&#10;&lt;p&gt;Dennoch. Die Qualität überzeugt mich noch nicht voll. Ich werde weiter probieren und meinen Fortschritt im Blog dokumentieren.&lt;/p&gt;&#10;</description></item><item><title>Github Actions: Fehler beheben</title><link>https://blog.schallbert.de/struggling-github-actions/</link><pubDate>Tue, 14 Feb 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/struggling-github-actions/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-02-14_github_actions-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Github Actions Workflow"&#10; title="Github Actions: Fehler beheben" /&gt;&#10;&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;&#10;&lt;p&gt;Warum ich einen Beitrag zu Github Actions schreibe, obwohl es im Internet zahllose Tutorials und Blogeinträge zum Thema gibt? Weil ich für mein letztes Software-Projekt &lt;a href="https://blog.schallbert.de/projects/qr-codengrave/"&gt;QR-codengrave&lt;/a&gt; eine Build Pipeline aufsetzen wollte und sich dies schwieriger gestaltete als angenommen. Sollte ich das an irgend einem Punkt in der Zukunft wiederholen müssen und erinnere mich nicht, wie ich damals in der IDE gebaut, getestet und veröffentlicht habe, kann die Pipeline weiterhelfen.&lt;/p&gt;&#10;&lt;p&gt;Ebenso, wenn sich meine IDE ändert oder meine Virtuelle Umgebung defekt ist, die &lt;code&gt;launch.json&lt;/code&gt; fehlt oder irgend etwas inkompatibel mit dem von mir verwendeten Compiler/Interpreter wird. Dennoch will ich in der Lage sein, Releases zu veröffentlichen, Bugfixes zu erstellen und Tests zu fahren.&lt;/p&gt;&#10;&lt;p&gt;Obwohl das nicht das übliche Argument ist, eine CI/CD Pipeline aufzubauen - normalerweise wird da zuerst die einfachere Zusammenarbeit mehrerer Menschen an einem gemeinsamen Programm(teil) genannt - ist der Anreiz für mich stark genug, mir mal Githubs Automation dafür, bekannt unter dem Namen &lt;a href="https://docs.github.com/en/actions" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Github Actions&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, auszuprobieren.&lt;/p&gt;&#10;&lt;p&gt;Meine Software ist zu unbedeutend um &amp;ldquo;Nightlies&amp;rdquo; zu bauen oder agil entwickelt zu werden, und daher werde ich die Pipeline auf &amp;ldquo;build&amp;rdquo;, &amp;ldquo;lint/static code checks&amp;rdquo;, &amp;ldquo;testing&amp;rdquo; und &amp;ldquo;deployment&amp;rdquo; beschränken und dies auch nur dann automatisch triggern, wenn es Änderungen auf dem Produktionsbranch gibt.&lt;/p&gt;&#10;&lt;h3 id="alles-klar-aber-warum-ein-extra-blogeintrag"&gt;Alles klar, aber warum ein extra Blogeintrag?&lt;/h3&gt;&#10;&lt;p&gt;Weil es mich so viel Zeit gekostet hat, alles richtig aufzusetzen. Ich habe dafür stundenlang Fehler suchen, oft fluchen und wütend Spaziergänge machen müssen, bevor ich meine in der IDE bereits getestete und lauffähige Software auch mit Github vereinen zu können.&lt;/p&gt;&#10;&lt;p&gt;Wäre ich klüger gewesen, hätte ich gleich zu Anfang nach Tools dafür recherchiert &lt;a href="https://github.com/nektos/act" target="_blank" rel="noopener noreferrer" class="external-link"&gt;wie dieses hier&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, sodass Github Actions auch lokal ausgeführt werden kann. Dies hätte mir die ständigen Pushes und Wartezeiten auf den nächsten, immer noch fehlerhaften Build ersparen können.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-02-14_gh_actions_struggle.jpg" alt="Image: Github Action failed runs" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="das-script"&gt;Das Script&lt;/h2&gt;&#10;&lt;p&gt;Github Actions benutzt &lt;code&gt;YAML&lt;/code&gt; um Befehle der Nutzer zu interpretieren. Die Verwendung ist sehr gut &lt;a href="https://docs.github.com/en/actions/learn-github-actions" target="_blank" rel="noopener noreferrer" class="external-link"&gt;dokumentiert&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und stellt einige &lt;a href="https://github.com/marketplace?type=actions" target="_blank" rel="noopener noreferrer" class="external-link"&gt;plug-and-play&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Beispiele bereit, die in vielen Szenarien und für die meisten Programmiersprachen direkt passen.&lt;/p&gt;&#10;&lt;p&gt;Die sogenannte &amp;ldquo;Workflowdatei&amp;rdquo; sieht so aus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-YAML" data-lang="YAML"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# This workflow will install Python dependencies, run tests and lint with a single version of Python&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;python_integrate&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;on&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;push&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;branches&lt;/span&gt;: [ &lt;span style="color:#e6db74"&gt;&amp;#34;master&amp;#34;&lt;/span&gt; ]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;pull_request&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;branches&lt;/span&gt;: [ &lt;span style="color:#e6db74"&gt;&amp;#34;master&amp;#34;&lt;/span&gt; ]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;permissions&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;contents&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;read&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;build&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runs-on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ubuntu-latest&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Set up Python 3.10&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/setup-python@v3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;python-version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;3.10&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Install dependencies&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; python -m pip install --upgrade pip&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; pip install flake8 pytest&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; pip install qrcodegen # Dependency install of qrcodegen&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; if [ -f requirements.txt ]; then pip install -r requirements.txt; fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Lint with flake8&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; # stop the build if there are Python syntax errors or undefined names&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Test with pytest&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;GabrielBB/xvfb-action@v1 &lt;/span&gt; &lt;span style="color:#75715e"&gt;# Diverts tkinter GUI to a virtual frame buffer (VFB)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; pytest&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dieses sieht nicht einmal groß anders aus als die Vorlage mit der ich gestartet bin. Die einzigen Anpassungen waren der nun vorhandene &lt;code&gt;pytest&lt;/code&gt; Aufruf sowie das Hinzufügen eines virtuellen Bildpuffers um Darstellungsprobleme meiner GUI zu lösen. Meine Tests schlugen nämlich fehl sobald die GUI gestartet wurde, da auf den Github Servern gar kein Display zur Verfügung steht.&lt;/p&gt;&#10;&lt;h2 id="github-action-runner-fehlermeldungen"&gt;Github Action runner Fehlermeldungen&lt;/h2&gt;&#10;&lt;p&gt;Die folgende Liste enthält die Fehler (und zugehörige Lösungen) mit denen ich konfrontiert wurde, bevor ich eine stabile CI aufsetzen konnte - erst nach fast 50 Durchläufen.&lt;/p&gt;&#10;&lt;h3 id="ordner-nicht-vorhanden"&gt;Ordner nicht vorhanden?&lt;/h3&gt;&#10;&lt;p&gt;&lt;em&gt;Output&lt;/em&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;opt&lt;span style="color:#f92672"&gt;/&lt;/span&gt;hostedtoolcache&lt;span style="color:#f92672"&gt;/&lt;/span&gt;Python&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3.10.9&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;x64&lt;span style="color:#f92672"&gt;/&lt;/span&gt;lib&lt;span style="color:#f92672"&gt;/&lt;/span&gt;python3&lt;span style="color:#ae81ff"&gt;.10&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;importlib&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;py:&lt;span style="color:#ae81ff"&gt;126&lt;/span&gt;: &lt;span style="color:#f92672"&gt;in&lt;/span&gt; import_module&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; _bootstrap&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_gcd_import(name[level:], package, level)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test&lt;span style="color:#f92672"&gt;/&lt;/span&gt;test_machinify_vector&lt;span style="color:#f92672"&gt;.&lt;/span&gt;py:&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;: &lt;span style="color:#f92672"&gt;in&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;module&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;from&lt;/span&gt; bin.platform.machinify_vector &lt;span style="color:#f92672"&gt;import&lt;/span&gt; MachinifyVector, Tool, EngraveParams&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;E &lt;span style="color:#a6e22e"&gt;ModuleNotFoundError&lt;/span&gt;: No module named &lt;span style="color:#e6db74"&gt;&amp;#39;bin&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Hintergrund&lt;/em&gt;:&#10;Ich habe meine Quelldateien unter &lt;code&gt;/bin&lt;/code&gt; abgelegt und meiner lokalen Instanz von Pyinstaller (das Werkzeug, was ich auswählte, um aus den Skripten eine ausführbare Datei zu erzeugen) zur Verfügung gestellt. Beim Upload konnte Github Actions die Pfade nicht auflösen und zeigte diesen Fehler an.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Lösung:&lt;/em&gt;&#10;Eine leere Datei mit Namen &lt;code&gt;__init__.py&lt;/code&gt; im &lt;code&gt;/bin&lt;/code&gt; Ordner ablegen. Dies zeigt alle im Ordner befindlichen Dateien als Package an, die so für Github Actions verfügbar werden.&#10;Die IDE hatte dieses Problem nicht, weil sie alle im Projektordner befindlichen Dateien automatisch indiziert.&lt;/p&gt;&#10;&lt;h3 id="yaml-syntaxfehler"&gt;YAML Syntaxfehler&lt;/h3&gt;&#10;&lt;p&gt;Diese Fehler entstanden, weil ich versucht hatte, mehrere Workflows in eine Datei zu schreiben. Github Actions scheint nur einen Workflow pro Datei zu akzeptieren.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-02-14_gh_actions_syntax.jpg" alt="Image: Github Action syntax error" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="fehler-pfad-nicht-gefunden"&gt;Fehler: Pfad-nicht-gefunden&lt;/h3&gt;&#10;&lt;p&gt;Ich hatte lange Probleme mit solchen Fehlern bedingt durch meine Ordnerstruktur:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-YAML" data-lang="YAML"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#ae81ff"&gt;assets &lt;/span&gt; &lt;span style="color:#75715e"&gt;# images, persistence file, etc.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#ae81ff"&gt;src &lt;/span&gt; &lt;span style="color:#75715e"&gt;# source files&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#ae81ff"&gt;test &lt;/span&gt; &lt;span style="color:#75715e"&gt;# pytest files for unit and integration testing&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#ae81ff"&gt;dist &lt;/span&gt; &lt;span style="color:#75715e"&gt;# build artifacts&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#ae81ff"&gt;build &lt;/span&gt; &lt;span style="color:#75715e"&gt;# build process files&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ich fand die echt schön aufgeräumt. Aber meine lokale IDE, PyInstaller und Github Actions bzw. dessen worker für Pyinstaller waren sich nicht einig, wie sow etwas aussehen soll. Immer würde sich eine der beteiligten Parteien beschweren, dass ein Pfad nicht existiere oder die Datei soundso nicht gelesen werden könne. Daher habe ich am Ende die Verwendung relativer Pfade verworfen und stattdessen pythons &lt;code&gt;importlib_resources&lt;/code&gt; verwendet.&lt;/p&gt;&#10;&lt;p&gt;Aber auch hier gab es Probleme mit Pyinstaller auf Github.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-02-14_gh_actions_paths.jpg" alt="Image: Github Action relative path error" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Letztendlich konnte ich das Problem erst lösen, als ich den Ordner für &lt;code&gt;assets&lt;/code&gt; in &lt;code&gt;src&lt;/code&gt; integrierte. Auf diese weise kann der &lt;code&gt;./&lt;/code&gt; Operator nicht falsch verstanden werden. Obwohl ich dieses Konstrukt nicht so schön finde, war ich immer weniger bereit, noch mehr Zeit dort hinein zu buttern. (Wenn Sie wissen, wie sich das Pfadhandling sowohl lokal als auch remote mit Eleganz lösen lässt, freue ich mich sehr über Ihren Beitrag auf meiner &lt;a href="https://github.com/Schallbert/schallbert.github.io/discussions" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Dissussionsseite&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;&#10;&lt;h3 id="tkinter-headless"&gt;Tkinter headless&lt;/h3&gt;&#10;&lt;p&gt;Spät im Entwicklungsprozess habe ich meinen Unit Tests ein paar Integrationstests zur Seite gestellt. So wollte ich sicherstellen, dass Popups die erforderlichen Callbacks an Main machen würden und umgekehrt Daten ausgetauscht werden können, sodass z.B. eine Werkzeugliste an das Konfigfenster übergeben werden kann.&lt;/p&gt;&#10;&lt;p&gt;Diese Tests liefen lokal zwar, aber mit einem Nachteil: Wenn ich einen Fehler oder eine Warnung erzeugte, ließ sich das &lt;code&gt;messageBox&lt;/code&gt;-Popup nicht automatisiert schließen. Es wollte unbedingt vom Nutzer selbst per Mausklick geschlossen werden. Dies ließ sich auch nicht durch einen Overlay oder Invoke der Click-Aktion umgehen.&lt;/p&gt;&#10;&lt;p&gt;Was also in der IDE nervig war, würde auf dem Server in der Automation zu Testabbrüchen führen. Also habe ich die bittere Pille geschluckt und eine Wrapperklasse um tkinters &lt;code&gt;messageBox&lt;/code&gt; geschrieben, sodass ich dort ein Mock injizieren konnte was dann wiederum nicht das echte Popup triggern würde.&lt;/p&gt;&#10;&lt;p&gt;Ausschnitt:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MsgBox&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Re-implementation due to testing purposes: With this trick, we are able to mock these windows&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; so we do not have to wait for users to manually close the dialog, unblocking the application again.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;showinfo&lt;/span&gt;(self, title, message):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; showinfo(title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;title, message&lt;span style="color:#f92672"&gt;=&lt;/span&gt;message)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;(self, title, message):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; showerror(title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;title, message&lt;span style="color:#f92672"&gt;=&lt;/span&gt;message)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Als das funktionierte, habe ich die Änderungen voller Hoffnung nach Github gepusht. Und erhielt folgenden, wunderschönen Fehler:&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;_tkinter.TclError: no display name and no $DISPLAY environment variable&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Das war zum Glück leicht zu verstehen - Tkinter wusste nicht, wo es die Fenster hinzeichnen sollte. Erscheint logisch, da der Server headless betrieben wird.&lt;/p&gt;&#10;&lt;p&gt;Also durchsuchte ich Foren und wurde mit nur einer Zeile Code belohnt, die dieses Problem sofort behob:&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;uses: GabrielBB/xvfb-action@v1 # Diverts tkinter GUI to a virtual frame buffer (VFB)&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Seitdem führt Github Actions bei Pull request auf &lt;code&gt;main&lt;/code&gt; alle tests aus und endlich ergießt sich ein langersehnter Regen grüner Häkchen kühlend auf meine heißgelaufenen Hirnwindungen. Was für ein schönes Gefühl.&lt;/p&gt;&#10;</description></item><item><title>QR-codengrave V1.1 / V1.2</title><link>https://blog.schallbert.de/qr-codengrave1-1/</link><pubDate>Sun, 12 Feb 2023 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/qr-codengrave1-1/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-02-12_qr-codengrave-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Splash screen of QR-codengrave V1.1"&#10; title="QR-codengrave V1.1 / V1.2" /&gt;&#10;&lt;h2 id="kurze-fakten-des-updates"&gt;Kurze Fakten des Updates&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Schnellerer Gravuralgorithmus und -Pfadoptimierung&lt;/li&gt;&#10;&lt;li&gt;Diverse Bugfixes&lt;/li&gt;&#10;&lt;li&gt;Bedienbarkeit verbessert&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="demo-video"&gt;Demo Video&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/jFD7nxftVHjUy6LKPCFE3N" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;QR-codengrave Quick Demo&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="version-11"&gt;Version 1.1&lt;/h2&gt;&#10;&lt;h3 id="algoritmusoptimierung"&gt;Algoritmusoptimierung&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Die Zeilen des QR-Codes werden nun horizontal eingelesen&lt;/li&gt;&#10;&lt;li&gt;Die Ausgabe der Pfade verläuft nun &amp;ldquo;rechts nach links&amp;rdquo;, danach &amp;ldquo;links nach rechts&amp;rdquo; und reduziert damit Maschinenbewegungen&lt;/li&gt;&#10;&lt;li&gt;Linien werden Punkten vorgezogen, da sie schneller gefertigt werden können&lt;/li&gt;&#10;&lt;li&gt;Linien werden nun vertikal und horizontal berechnet, die längere Linie bekommt den Vorzug&lt;/li&gt;&#10;&lt;li&gt;Eilgänge werden nun per XY-Vektor gefahren und nicht mehr nur eindimensional, was Zeit spart&lt;/li&gt;&#10;&lt;li&gt;Der neue Pfadalgorithmus benötigt 20% weniger Maschinenzeit&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2023-02-12_qr-codengrave_algo.jpg" alt="Image: Algorithm comparison 1.0 vs. 1.1" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="bugfixes"&gt;Bugfixes&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Bei nicht vorliegendem Datensatz &lt;code&gt;Persistence.dat&lt;/code&gt; wird das Startbild nicht angezeigt (&lt;code&gt;qruwu.jpg&lt;/code&gt;)&lt;/li&gt;&#10;&lt;li&gt;Crash bei Klick auf &lt;code&gt;Stop Draw&lt;/code&gt; wenn zuvor keine Zeichnung angefragt wurde&lt;/li&gt;&#10;&lt;li&gt;Crash bei Hinzufügen eines Werkzeuges bei leerer Werkzeugliste&lt;/li&gt;&#10;&lt;li&gt;Wird das Programm ohne Werkzeugauswahl gestartet, ist das dropdown-Menü gesperrt&lt;/li&gt;&#10;&lt;li&gt;Fehler in der QR-Code Größenschätzung&lt;/li&gt;&#10;&lt;li&gt;Fehler in der Blockbreitenberechnung bei Verwendung von V-Fräsern&lt;/li&gt;&#10;&lt;li&gt;Das gegenwärtig ausgewählte Werkzeug wird bei Start der Anwendung nicht im Dropdown-Feld angezeigt&lt;/li&gt;&#10;&lt;li&gt;QR-codes können größer dargestellt werden der Anzeigebereich&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="bedienbarkeit"&gt;Bedienbarkeit&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Default Werkzeugname geändert von &lt;code&gt;name&lt;/code&gt; nach &lt;code&gt;default&lt;/code&gt; zur besseren Verständlichkeit&lt;/li&gt;&#10;&lt;li&gt;Fräser mit geraden Schneiden (also kein V-cut) ist jetzt das Standardwerkzeug&lt;/li&gt;&#10;&lt;li&gt;Erstellen eines Offsets für den XY-Nullpunkt ist jetzt intuitiver gelöst (Y- nach unten, X+ nach Rechts)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="version-12"&gt;Version 1.2&lt;/h2&gt;&#10;&lt;p&gt;Kleine Änderungen zu 1.1, am Wichtigsten die Behebung eines Bugs, bei dem die Aufforderung zum Werkzeugwechsel verloren gehen kann. Außerdem werden die Winkel von V-Fräsern jetzt bei Berechnung von Pixelbreite und Größe der QR-Codes mit einbezogen.&lt;/p&gt;&#10;&lt;h3 id="bugfixes-1"&gt;Bugfixes&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Werkzeugauswahl &lt;code&gt;Tx&lt;/code&gt; und Werkzeugwechselkommando &lt;code&gt;M06&lt;/code&gt; befinden sich nun in derselben Zeile G-Code wie üblich und können so wie gewünscht Makros auslösen.&lt;/li&gt;&#10;&lt;li&gt;Das beim letzten Speichern verwendete Werkzeug wird nicht angezeigt, stattdessen das erste Werkzeug der Liste&lt;/li&gt;&#10;&lt;li&gt;Programmabsturz wenn versucht wird, den Jobstatus zu aktualisieren obwohl keine Gravurparameter vorliegen&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="bedienbarkeit-1"&gt;Bedienbarkeit&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Versionsnummer als Kommentar in G-code hinzugefügt&lt;/li&gt;&#10;&lt;li&gt;Prefix &lt;code&gt;qr_&lt;/code&gt; der Zieldatei im Datei-Speichern-Dialog hinzugefügt&lt;/li&gt;&#10;&lt;li&gt;Genauigkeit der Maschinenzeitabschätzung verbessert&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="download"&gt;Download&lt;/h2&gt;&#10;&lt;p&gt;Der aktuelle Release ist &lt;a href="https://github.com/Schallbert/QR-codengrave/releases" target="_blank" rel="noopener noreferrer" class="external-link"&gt;hier&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zum Download verfügbar. Das Programm ist für die private Anwendung sowie Schulungszwecke kostenlos. Für weitere Informationen und Projektdokumentation stehen die &lt;a href="https://github.com/Schallbert/QR-codengrave" target="_blank" rel="noopener noreferrer" class="external-link"&gt;readme&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; sowie eine &lt;a href="https://blog.schallbert.de/projects/qr-codengrave/"&gt;Projektseite&lt;/a&gt; zur freien Verfügung.&lt;/p&gt;&#10;&lt;h2 id="upgrade-von-einer-früheren-version"&gt;Upgrade von einer früheren Version&lt;/h2&gt;&#10;&lt;p&gt;Es wird empfohlen, die Persistenzdatei von der alten Version in die neue Version zu übernehmen. Auf diese Weise werden gespeicherte Werkzeuglisten, Gravurparameter und XY-Offsets portiert. Und so geht&amp;rsquo;s:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Wechseln Sie in den Ordner, wo sich das QR-codengrave Programm befindet. Anschließend navigieren Sie zu &lt;code&gt;src/assets&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Kopieren Sie die Datei &lt;code&gt;Persistence.dat&lt;/code&gt; in einen temporären Ordner oder z.B. auf Ihren Desktop.&lt;/li&gt;&#10;&lt;li&gt;Entfernen Sie die alte Version durch Löschen des QR-codengrave Ordners.&lt;/li&gt;&#10;&lt;li&gt;Laden Sie die letzte Version herunter und verschieben Sie sie zur gewünschten Position auf Ihrem Rechner.&lt;/li&gt;&#10;&lt;li&gt;Verschieben Sie nun die vorab zwischengespeicherte &lt;code&gt;Persistence.dat&lt;/code&gt; in den &lt;code&gt;src/assets&lt;/code&gt; Ordner des aktualisierten Programms.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;</description></item><item><title>Steckbrett-Lagerfeuer</title><link>https://blog.schallbert.de/led-fireplace-diy/</link><pubDate>Sat, 10 Dec 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/led-fireplace-diy/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/fireplace/fireplace-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: A small electronics Breadboard with microcontroller, LEDs etc. on to make a fireplace simulation."&#10; title="Steckbrett-Lagerfeuer" /&gt;&#10;&lt;h2 id="materialliste"&gt;Materialliste&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Gegenstand&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Menge&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;~Preis [€]&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;LED: Warmweiß, z.B. Ifwd=30mA, 3lm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1,50&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;LED: Gelb, z.B. Ifwd=35mA, 1lm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,60&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;LED: Rot, z.B. Ifwd=35mA, 0.4lm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,30&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Arduino Nano Atmega328&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;18&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Steckbrett klein, z.B. 170 pins&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,70&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Brückendrähte, pin-pin, z.B. 5cm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;10&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3,30&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Dazu: Ein paar weitere Drähte, Mini USB-Kabel, Powerbank / USB-Ladegerät&lt;/p&gt;&#10;&lt;h2 id="werkzeuge"&gt;Werkzeuge&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Optional: Heißklebepistole&lt;/li&gt;&#10;&lt;li&gt;Optional: Lötkolben&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="aufbau"&gt;Aufbau&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/fireplace/fireplace_detail.jpg" alt="Image: fireplace breadboard design detail view" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/f9/LED%2C_5mm%2C_green_%28en%29.svg/432px-LED%2C_5mm%2C_green_%28en%29.svg.png" alt="Image: LED closeup" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;LED aus der Nähe betrachtet&lt;/span&gt;&lt;a href="https://commons.wikimedia.org/wiki/File:LED,_5mm,_green_%28en%29.svg#file" class="attr-link" aria-label="Attribution 1"&gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Die rechte Pin-Seite des Arduino Nano mit der linken Seite des Steckbretts verbinden. Dabei darauf achten, dass alle Pins auf gleicher Höhe sind und möglichst nicht mit den Fingern zu berühren, da die Gefahr einer elektrostatischen Entladung besteht, die zu Beschädigung führen könnte.&lt;/li&gt;&#10;&lt;li&gt;Die Anoden (&lt;code&gt;+&lt;/code&gt;) der LEDs mit den Ausgängen &lt;code&gt;D2 bis D9&lt;/code&gt; auf dem Steckbrett verbinden. Die Anode ist meistens der Pin der LED, der nicht mit der Lichtquelle belegt ist (im Bild oben wäre das links).&lt;/li&gt;&#10;&lt;li&gt;Die Kathoden (&lt;code&gt;-&lt;/code&gt;) der LEDs auf der rechten Seite des Steckbretts auf gleicher Höhe einstecken.&lt;/li&gt;&#10;&lt;li&gt;Alle Kathoden mit Hilfe von Überbrückungsdrähten verbinden. Alternativ können die Kathoden auch gleich zusammen gelötet oder mittels einer Verteilerbrücke, wie in meinem Beispiel oben verwendet, durchverbunden werden.&lt;/li&gt;&#10;&lt;li&gt;Verbinden Sie die &amp;ldquo;gemeinsame Kathode&amp;rdquo; mit Hilfe eines Brückendrahtes mit einem der &amp;ldquo;GND&amp;rdquo;-Pins der Mikrocontroller-Platine.&lt;/li&gt;&#10;&lt;li&gt;Die Anoden der LEDs sollten sich nicht berühren. Das kann man einfach sicherstellen, indem sie mit etwas Heißkleber fixiert werden.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="programmierung"&gt;Programmierung&lt;/h2&gt;&#10;&lt;p&gt;Der Code ist so einfach, dass ich ihn hier in Gänze herunterschreiben kann kann. Kopieren Sie ihn in Ihre Lieblings-IDE, programmieren Sie Ihr Board und schon sind Sie fertig!&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Disclaimer&lt;/em&gt;: Dieser Code ist &amp;ldquo;quick&amp;rsquo;n&amp;rsquo;dirty&amp;rdquo; und hat viel Potential für Refactoring. Zum Beispiel sind die Pin-IDs hartkodiert, ich verwende nicht die tollen und einfachen Features von C++11 wie &lt;a href="https://en.cppreference.com/w/cpp/language/range-for" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Range-based for loop&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und der Geschwindigkeitsalgorithmus wird ziemlich oft die untere Grenze erreichen und kein zufälliges Verhalten zeigen.&#10;Ich freue mich über Ihre Verbesserungsvorschläge auf meiner &lt;a href="https://github.com/Schallbert/schallbert.github.io/discussions" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Github discussions&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Seite.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#include&lt;/span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;Arduino.h&amp;gt;&lt;/span&gt;&lt;span style="color:#75715e"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#define MAXLONG 0xFFFFFFFF&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#define SPEED_HIGHLIMIT 1000 &lt;/span&gt;&lt;span style="color:#75715e"&gt;// 500Hz&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#define SPEED_LOWLIMIT 0x1FFF &lt;/span&gt;&lt;span style="color:#75715e"&gt;// 20Hz&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;calcFlicker&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;uint32_t&lt;/span&gt; randomFlicker);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;writeLed&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; columnCode, &lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt; speed);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;uint32_t&lt;/span&gt; iRNG &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;setup&lt;/span&gt;() &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;randomSeed&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;analogRead&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;));&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pinMode&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, OUTPUT);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pinMode&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, OUTPUT);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pinMode&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;, OUTPUT);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pinMode&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, OUTPUT);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pinMode&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;, OUTPUT);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pinMode&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;, OUTPUT);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pinMode&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;, OUTPUT);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pinMode&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;9&lt;/span&gt;, OUTPUT);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iRNG &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;loop&lt;/span&gt;() &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iRNG &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (iRNG &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;^&lt;/span&gt; (&lt;span style="color:#f92672"&gt;-&lt;/span&gt;(iRNG &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1u&lt;/span&gt;) &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0xd0000001u&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;calcFlicker&lt;/span&gt;(iRNG);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/*&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; This function takes a 32bit random value,&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; and moves this value along a window of an 8bit &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; flicker output value.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;*/&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;calcFlicker&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;uint32_t&lt;/span&gt; randomFlicker)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; currentFlicker &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt;)(randomFlicker &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0xFF&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt; randomSpeed &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;min&lt;/span&gt;(SPEED_LOWLIMIT, (&lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt;)(randomFlicker &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; SPEED_HIGHLIMIT));&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt;; &lt;span style="color:#f92672"&gt;++&lt;/span&gt;i) &lt;span style="color:#75715e"&gt;// 24 because of 32bit length of random, may not run empty&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;writeLed&lt;/span&gt;(currentFlicker, randomSpeed);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; randomFlicker &lt;span style="color:#f92672"&gt;=&lt;/span&gt; randomFlicker &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/*&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; this function outputs a code to an LED column of 8 LEDs&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; connected to arduino&amp;#39;s pins with a selectable delay.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;*/&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;writeLed&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; columnCode, &lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt; speed)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;digitalWrite&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, columnCode &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x01&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;digitalWrite&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, columnCode &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x02&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;digitalWrite&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;, columnCode &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x04&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;digitalWrite&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, columnCode &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x08&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;digitalWrite&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;, columnCode &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x10&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;digitalWrite&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;, columnCode &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x20&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;digitalWrite&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;, columnCode &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x40&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;digitalWrite&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;9&lt;/span&gt;, columnCode &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x80&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;delayMicroseconds&lt;/span&gt;(speed);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="testen"&gt;Testen&lt;/h2&gt;&#10;&lt;p&gt;Sobald Sie das Gerät über USB einschalten, sollte die LED-Flackersequenz beginnen. Es sollte sich ein ruhiges Flackern einstellen. Wenn man im Regal einen leeren Platz findet und ein paar Zweige zur Hand hat, könnte das doch ganz schön aussehen, oder?&#10;Mit einer Powerbank kann man dieses Lagerfeuer auf dem Steckbrett sogar überall hin mitnehmen. Das war&amp;rsquo;s, viel Spaß damit!&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/projects/fireplace/"&gt;Zur Hauptseite des Projektes&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>Vakuumpumpen</title><link>https://blog.schallbert.de/cnc-vacuum-pumps/</link><pubDate>Fri, 28 Oct 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cnc-vacuum-pumps/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_vacuumpump-thumb.jpg"&#10; class="post-cover"&#10; alt="Image of a Sidechain compressor vacuum pump"&#10; title="Vakuumpumpen" /&gt;&#10;&lt;h2 id="was-ist-das"&gt;Was ist das?&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_manometer.jpg" alt="Image: Vacuum intake manifold" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Eine Vakuumpumpe ist ein Gerät, welches ein Gefäß evakuieren kann. Es ist also in der Lage, eine Menge an Gasatomen aus einem Gefäß zu entnehmen, sodass dieses schließlich weniger Atome pro Volumeneinheit enthält als das umgebende Medium. Dies erzeugt in dem Gefäß einen Unterdruck. Hier auf der Erde &lt;a href="https://physics.stackexchange.com/questions/161107/why-is-it-impossible-to-have-a-perfect-vacuum" target="_blank" rel="noopener noreferrer" class="external-link"&gt;scheint es nicht möglich zu sein&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, die Zahl der Atome in einem Gefäß auf null zu senken, aber das ist natürlich für unsere Zweck auch nicht erforderlich. Ein technisches Grobvakuum ist hier vollkommen ausreichend.&lt;/p&gt;&#10;&lt;p&gt;Es gibt verschiedene Typen von Vakuumpumpen, jeder optimiert für andere Einsatzbedingungen. Doch mehr dazu später.&lt;/p&gt;&#10;&lt;h2 id="wie-kann-die-vakuumpumpe-werkstücke-festhalten"&gt;Wie kann die Vakuumpumpe Werkstücke festhalten?&lt;/h2&gt;&#10;&lt;p&gt;Für Niederhalten und Spannen von Werkstücken mittels Vakuumpumpe wird ein Vakuumtisch (siehe mein &lt;a href="https://blog.schallbert.de/why-vacuum-table/"&gt;Beitrag zum Thema&lt;/a&gt;) beziehungsweise eine Unterdruckklemmvorrichtung benötigt. Hier sind wir an der Druckdifferenz zur Umgebungsluftdruck (&lt;code&gt;103000Pa = 10.3N/cm² = 1kg/cm²&lt;/code&gt;) interessiert. Dies ist die maximale Niederhaltekraft pro Fläche, die mit einem Vakuumtisch und dazu passend ausgelegter Pumpe theoretisch erreicht werden kann.&lt;/p&gt;&#10;&lt;h3 id="volumenförderstrom-vs-unterdruck"&gt;Volumenförderstrom vs. Unterdruck&lt;/h3&gt;&#10;&lt;p&gt;Die wichtigsten Parameter einer Vakuumpumpe sind - wie bei anderen Pumpentypen auch - die maximal erreichbare Förderleistung, gemessen in &lt;code&gt;m³/h&lt;/code&gt; (bei niedriger Druckdifferenz), sowie geringster erreichbarer Absolutdruck im vakuumierten Gefäß in &lt;code&gt;hPa&lt;/code&gt; (bei niedrigem Volumenstrom) auf der Saugseite. Die Druckseite ist bei Vakuumpumpen logischerweise uninteressant.&lt;/p&gt;&#10;&lt;p&gt;Das erinnert mich ein wenig an die elektrische Welt, wo z.B. Leerlaufspannung (max. Druckdifferenz) und Kurschlussstrom (max. Volumenstrom) eine Spannungsquelle charakterisieren und es darum geht, einen optimalen Arbeitspunkt zu finden.&lt;/p&gt;&#10;&lt;p&gt;Wenn man auf eine Spannung aus ist, kann man eine Quelle nur mit einem geringen Strom belasten und so die Spannung oben halten. Anders herum kann bei geringer Spannung ein hoher Strom aus der Quelle bezogen werden. In jedem Falle ist auch bei der Vakuumpumpe ein optimaler Arbeitspunkt zu finden und den Pumpentyp entsprechend auszuwählen. Einige Pumpentypen nämlich sind auf Durchsatz optimiert und damit für andere Zwecke gut geeignet als Pumpen, die große Unterdrücke bei geringerem Volumenstrom erreichen.&lt;/p&gt;&#10;&lt;p&gt;Wird viel von beidem benötigt, steigen Kosten und Energiebedarf der Pumpe stark an - es kommt also wieder einmal darauf an, was man braucht, denn die &amp;ldquo;Alleskönnerpumpe&amp;rdquo; ist ökonomisch oft nicht sinnvoll.&lt;/p&gt;&#10;&lt;h3 id="charakteristika-der-pumpen"&gt;Charakteristika der Pumpen&lt;/h3&gt;&#10;&lt;p&gt;Ich habe mir mal die Mühe gemacht, Volumenstrom über Druckdifferenz in einem Diagramm einzutragen und für verschiedene Pumpentypen in ähnlicher Leistungsklasse auszuwerten. Dann zog ich eine Linie durch die beiden Punkte (sicher, das ist nur eine Annäherung, der wahre Verlauf ist eher asymptotisch), um sie besser vergleichbar zu machen.&lt;/p&gt;&#10;&lt;p&gt;Für das Beispiel habe ich willkürlich eine elektrische Leistung der Pumpe von rund &lt;code&gt;1.5kW&lt;/code&gt; angenommen. Die Werte habe ich aus online abrufbaren Datenblättern verschiedener Pumpenhersteller entnommen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_pump_type_compare.jpg" alt="Image: XY-line graph for pump types of same power" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Natürlich ist das nur eine grobe Annäherung und gilt eher für Pumpentypen mit großem Durchsatz als für diejenigen mit hoher Druckdifferenz. Die Funktion letzterer sieht eher aus wie ein hyperbolisches Parabolid (siehe Abbildung weiter unten), da erreichbare Druckdifferenz von Leckströmen stark gemindert wird.&lt;/p&gt;&#10;&lt;p&gt;Wir können solch eine Kurve gut für die Arbeitspunktbestimmung verwenden: Der Hersteller des Vakuumtisch empfiehlt meist den erforderlichen Volumenstrom für das jeweilige Produkt. Dieser wird nun auf der Linie markiert, und schon ergibt sich das dann erreichbare Vakuum. Am Schluss des Artikels komme ich hierauf zurück und versuche, ein paar Empfehlungen zu geben.&lt;/p&gt;&#10;&lt;h2 id="pumpentypen"&gt;Pumpentypen&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_pump_types.jpg" alt="Image: Comparison of vaccuum pump leakage behavior" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Es gibt viele verschiedene Pumpentypen zur Vakuumerzeugung in der Industrie. Sie haben teils verschiedene Wirkprinzipien und selbstverständlich unterschiedliche Einsatzzwecke. Ich konzentriere mich hier auf die vier Typen, die im Hobbybereich meiner Ansicht nach am Häufigsten zum Einsatz kommen.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Radialgebläse (Sehr hoher Volumenstrom)&lt;/li&gt;&#10;&lt;li&gt;Seitenkanalverdichter (Hoher Volumenstrom)&lt;/li&gt;&#10;&lt;li&gt;Kolbenpumpe (Tiefes Vakuum)&lt;/li&gt;&#10;&lt;li&gt;Drehschieberpumpe (Tiefes Vakuum)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="radial-blower"&gt;Radialgebläse&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_radialblower.jpg" alt="Image: Radial blower symbol view" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Radialgebläse beschleunigen die Luft zwischen ihren Schaufeln. Luft wird axial in Mitte des Gebläses angesaugt und durch Fliehkräfte radial nach außen gedrückt, wo sie über einen Kanal an einer Stelle radial austreten kann.&lt;/p&gt;&#10;&lt;p&gt;Radialgebläse werden für sehr viele Anwendungen gebaut. Sie kommen beispielsweise in Großfeuerungen von Kraftwerken, in Hüpfburgen und dem Automotive-Bereich vor und waren vor fast zwei Jahrzehnten sogar auf Grafikkarten häufiger anzutreffen (Ich hatte mal einen auf meiner ATI Radeon X1900XT). Sie sind sehr einfach im Aufbau, kostengünstig herzustellen und sind in der Lage, einen unglaublich hohen Luftdurchsatz bereitzustellen. Nachteilig sind die hohen Laufgeräuschpegel und die im Vakuumbereich relevanten, geringen erreichbaren Unterdrücke.&lt;/p&gt;&#10;&lt;p&gt;Wenn allerdings mit Plattenmaterial und großflächigen Werkstücken gearbeitet wird, kann diese Vakuumlösung trotzdem sehr gut passen. Im Hobbybereich können hier oft sogar günstige Staubsauger (mit Fremdkühlung durch einen eigenen Lüfter) verwendet werden.&lt;/p&gt;&#10;&lt;h3 id="seitenkanalverdichter"&gt;Seitenkanalverdichter&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_sidechaincompressor.jpg" alt="Image: Sidechain compressor symbol view" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Seitenkanalverdichter kombinieren einen hohen Volumenstrom mit mittleren Druckdifferenzen. Man kann sie als ein- oder mehrstufige Systeme erwerben (Serienschaltung). Dadurch sind sie gut auf die Bedürfnisse der Kundschaft anpassbar. Wie Radialgebläse interagieren sie nur mit dem Medium und verschleißen nicht durch Teil-zu-Teil-Reibung. Dadurch sind sie ebenso wartungsarm und tolerieren auch Dämpfe und staubige Umgebungen.&lt;/p&gt;&#10;&lt;p&gt;Wünscht man einen Seitenkanalverdichter mit hohen Druckdifferenzen, muss man einen sehr leistungsfähigen Motor wählen, denn die charakteristische Kurve verläuft recht flach. Die Geräuschbelästigung kann durch beidseitige Verwendung von Schalldämpfern gut reduziert werden. Dennoch sind sie bei vergleichbaren Leistungswerten teurer als Radialgebläse.&lt;/p&gt;&#10;&lt;p&gt;In dieser Aufzählung sind sie die eierlegende Wollmilchsau: Sie können einen passablen Unterdruck bei hohem Volumenstrom erzeugen, ihr Preis ist moderat, sie benötigen kaum Wartung und ihr unangenehmes, turbinenartiges Geräusch lässt sich mit ein paar technischen Maßnahmen in den Griff bekommen.&lt;/p&gt;&#10;&lt;p&gt;Ich fand diese tiefgehende Erklärung von Seitenkanalgebläsen ebenfalls nützlich: &lt;a href="https://gutmbh.de/SKVM.htm" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Link geht zur &amp;ldquo;GutmbH&amp;rdquo;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Und dieses &lt;a href="https://www.youtube.com/watch?v=8JpBVmqjSu0" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Youtube-Video&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zeigt einen herrlich verspielten Einsatz als Verdichter.&lt;/p&gt;&#10;&lt;h3 id="kolbenpumpe"&gt;Kolbenpumpe&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_pistonpump.jpg" alt="Image: Piston pump symbol view" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Der Klassiker. Kolbenpumpen werden sowohl für Kompressoren als auch für Vakuumpumpen verwendet. Sie stellen bei vergleichbar geringen Volumenströmen hohe Druckdifferenzen bereit, benötigen nicht viel Platz in der Werkstatt, und sind schon allein durch die hohen Produktionsvolumen günstig in der Anschaffung.&lt;/p&gt;&#10;&lt;p&gt;Auf der anderen Seite bieten sie nur einen pulsierenden Luftstrom und sind konzeptbedingt eher laut. Überdies können nur speziell dafür angefertigte Modelle mit Flüssigkeiten (Flutkühlung) oder Stäuben umgehen und benötigen mehr Wartung als die obengenannten Typen. Ich habe längst nicht so viele Anwendungsbeispiele dieses Pumpentyps für Vakuumtische gefunden wie bei den anderen. Das heißt aber nicht, dass ich sie für diese Einsatzzwecke als ungeeignet einstufe.&lt;/p&gt;&#10;&lt;h3 id="drehschieberpumpen"&gt;Drehschieberpumpen&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_rotaryvanepump.jpg" alt="Image: Rotary vane pump symbol view" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Drehschieberpumpen teilen sich wiederum in zwei Klassen: Trockenlaufend und ölgeschmiert.&#10;Während trockenlaufende Drehschieberpumpen gleicher Leistung bei etwas höherem Durchsatz ähnliche Unterdrücke erzeugen können wie Kolbenpumpen, sind ihre ölgeschmierten Pendants in der Lage, deutlich größere Unterdrücke zu erreichen. Zudem benötigen sie weniger Energie zum Betrieb als ihre trockenlaufenden Kollegen.&lt;/p&gt;&#10;&lt;p&gt;Auf der anderen Seite benötigen sie eine Warmlaufzeit für das Öl und einen Entgasungszyklus vor ihrer Abschaltung. Zudem benötigen sie durch Ölwechsel und leichte Ölnebel in der Abluft mehr Wartungsaufwand.&lt;/p&gt;&#10;&lt;p&gt;Das Wirkprinzip: Ein Motor treibt eine auf seiner Welle befindliche geschlitzte Scheibe an, welche exzentrisch in einem Gehäuse montiert ist. In den Schlitzen sind kleine Platten eingelassen, die sich radial frei bewegen können. Startet der Motor nun, werden die Platten durch Fliehkräfte an die Wände der Kammer gedrückt und schließen diese beinahe luftdicht gegen die anderen Kammern ab. Durch die Exzentrizität ist das Volumen dieser Kammern über einen Umlauf variabel und verkleinert sich in Richtung Auslass, sodass auf der Saugseite ein Unterdruck und am Auslass ein Überdruck entsteht.&lt;/p&gt;&#10;&lt;p&gt;Drehschieberpumpen sind nur für kompressible Medien geeignet, benötigen Staubabscheider am Einlass und vertragen keine Flüssigkeiten im Medium. Dafür liefern sie einen kontinuierlichen Saugstrom und sind im Betrieb vergleichsweise leise. Die Drehschieber sind Verschleißteile, die irgendwann ersetzt werden müssen - besonders bei den trockenlaufenden Geräten. Im Vergleich zu Seitenkanalverdichtern gleichen Volumenstroms liegen ihre Kosten zur Zeit des Verfassens dieses Artikels um den Faktor zwei bis drei höher.&lt;/p&gt;&#10;&lt;p&gt;Weitere Details gefällig? Wie oben bereits erwähnt halte ich diese Seite von &lt;a href="https://gutmbh.de/Vakuumpumpen.htm" target="_blank" rel="noopener noreferrer" class="external-link"&gt;GutmbH&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; für lesenswert.&lt;/p&gt;&#10;&lt;h2 id="das-werkstück-bestimmt-die-pumpe"&gt;Das Werkstück bestimmt die Pumpe&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_table_operating_principle.jpg" alt="Image: Vacuum table principle of operation - pressure difference causes downforce" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Um eine gute Wahl für eine Vakuumpumpe treffen zu können, sollte der Einsatzzweck also klar und der zu verwendende Vakuumtisch eine bekannte Größe sein.&lt;/p&gt;&#10;&lt;p&gt;Wie in der Zeichnung zu erkennen ist, hängt die erreichbare Niederhaltekraft stark von der Größe der Oberfläche des Werkstücks ab - je kleiner sie ist, desto größer muss der Unterdruck sein, den die Pumpe liefert, um dieselbe Niederhaltekraft zu erreichen. Leider ist der Unterdruck wie oben beschrieben durch das absolute bzw. das durch die gewählte Pumpe erreichbare technische Grobvakuum limitiert. Trotzdem können Vakuumtische sehr homogene Niederhaltekräfte bieten.&lt;/p&gt;&#10;&lt;p&gt;Was wir vermeiden wollen, ist, dass das Werkstück durch das Werkzeug beim Eingriff vom Maschinentisch gelöst wird. Ebenfalls möchten wir Parallelverschiebung oder Drehung des Werkstücks durch das Werkzeug verhindern. Um ein paar Anhaltspunkte zu bekommen, können wir zuerst die Niederhaltekraft für unser Werkstück berechnen. In einem zweiten Schritt dann können wir abschätzen, wie viel Widerstand uns dies gegen laterale Verschiebung oder Drehung bietet. Solange die vom Werkzeug in das Werkstück eingebrachte Kraft unter dieser Losbrechkraft liegt, sind wir auf der sicheren Seite und können möglicherweise eine preisgünstigere Pumpe wählen.&lt;/p&gt;&#10;&lt;h3 id="niederhaltekraft"&gt;Niederhaltekraft&lt;/h3&gt;&#10;&lt;p&gt;Die Klemmkraft, die das Werkstück auf dem Maschinentisch festhält, ist leicht zu berechnen:&lt;/p&gt;&#10;$$f_{clamp}= F_N = \Delta p A + F_g $$&lt;p&gt;Sie wird in &lt;code&gt;Newton&lt;/code&gt; angegeben und ergibt sich aus der Druckdifferenz zwischen Ober- und Unterseite des Werkstücks und der Erdanziehungskraft, die das Werkstück nach unten drückt.&lt;/p&gt;&#10;&lt;p&gt;Der erforderliche Wert hängt stark von der Werkzeuggröße- und Geometrie, Werkstückmaterial sowie vom Vorschub und gewählter Drehzahl ab. So wird wesentlich mehr Klemmkraft bei harten und zähen Materialien wie Hochdrucklaminaten oder Stahl benötigt, wenn Werkzeuge mit großen Durchmessern oder Drallwinkeln eingesetzt werden, die zudem bei großen Vorschüben und geringer Drehzahl durch das Material gehen als bei Weichhölzern, wo gern mit hohen Drehzahlen und gerade geschliffenen Fräsern gearbeitet wird.&lt;/p&gt;&#10;&lt;p&gt;Die Berechnung der &lt;a href="https://de.wikipedia.org/wiki/Zerspankraft#Komponenten_der_Zerspankraft" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Zerspankräfte&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, insbesondere der hier für uns interessanten Vorschubkraft, würde einen eigenen Artikel erforderlich machen. Für den Hobbybereich sind meine Erfahrungswerte, dass Werkstücke kleiner als &lt;code&gt;80x80mm&lt;/code&gt; nur mit großen Unterdrücken um &lt;code&gt;-0.8Bar&lt;/code&gt; gehalten werden können, wenn man nicht gerade im Schneckentempo arbeiten möchte. Andersherum reichen bei &lt;code&gt;600x600mm&lt;/code&gt; großen Platten ohne Durchfräsungen bereits &lt;code&gt;-0.1Bar&lt;/code&gt;, um die Platte sicher zu halten.&lt;/p&gt;&#10;&lt;h3 id="parallelverschiebung"&gt;Parallelverschiebung&lt;/h3&gt;&#10;&lt;p&gt;Um Niederhaltekraft in Widerstand gegen Parallelverschiebung umzurechnen, führe ich hier den &lt;a href="https://en.wikipedia.org/wiki/Friction#Coefficient_of_friction" target="_blank" rel="noopener noreferrer" class="external-link"&gt;statischen Reibungskoeffizienten&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; \(µ_s\) ein.&lt;/p&gt;&#10;&lt;p&gt;Nehmen wir an, wir legen eine Lage Gummi zwischen unser (metallisches) Werkstück und den Vakuumtisch. Nehmen wir weiter einen statischen Reibungskoeffizienten &lt;code&gt;µs = 0.7&lt;/code&gt; zwischen Gummi und Werkstück an - &lt;a href="https://www.schweizer-fn.de/stoff/reibwerte/reibwerte.php" target="_blank" rel="noopener noreferrer" class="external-link"&gt;den Wert habe ich online gefunden&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; - was für mich aus mehreren Gründen nach einer konservativen Schätzung aussieht: Erstens klammern wir die Oberflächenrauigkeit von Gummimatte und Werkstück aus, und zweitens wird das Werkstück durch den Unterdruck etwas in die Gummimatte hineingedrückt, was die Losbrechkräfte durch Formschluss nochmal deutlich erhöht.&lt;/p&gt;&#10;&lt;p&gt;Egal, denn alles was zu tun bleibt, ist die oben berechnete Kraft mit dem Reibungskoeffizienten zu multiplizieren.&lt;/p&gt;&#10;$$F_s = F_N µ_s$$&lt;p&gt;Hier kann man also an zwei Stellschrauben drehen: Der Materialpaarung und dem von der Pumpe bereitgestellten Anpressdruck. Es gibt auch spezielle Vakuumvliese, welche besonders hohe Reibungskoeffizienten besitzen und bei Anlegen des Vakuums durch Eindrücken des Werkstücks in das Vlies für zusätzlichen Widerstand gegen Verschiebung und Rotation sorgen.&lt;/p&gt;&#10;&lt;h3 id="rotation"&gt;Rotation&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_rotatingworkpiece.jpg" alt="Image: Worst-case scenario for rotating a workpiece out of position" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Hier wird die Berechnung schwierig. Interessanterweise wird weniger Kraft benötigt, ein Werkstück von seiner Position wegzudrehen, als es parallel zu verschieben. Das kann man sich aber zum Glück recht leicht klarmachen:&lt;/p&gt;&#10;&lt;p&gt;Wenn Sie z.B. Ihr Mobiltelefon auf dem Tisch um seinen Mittelpunkt herum drehen (etwa durch Berührung der rechten oberen Kante mit dem Zeigefinger), müssen nicht alle denkbaren Punkte auf dem Smartphone denselben Weg zurücklegen. Punkte, die sich näher am Drehpunkt befinden, legen eine geringere Distanz für den selben überstrichenen Winkel zurück als solche, die weiter in Richtung Kante des Smartphones liegen. Für eine Paralellverschiebung hingegen muss jeder Punkt einen gleich großen Weg zurücklegen, für den ein entsprechender Energieeinsatz erforderlich ist.&lt;/p&gt;&#10;&lt;p&gt;Für das Spannen auf dem Vakuumtisch sind lange, schmale Werkstücke besonders anspruchsvoll. Sie bieten mit ihren langen Kanten einen großen Hebel und bieten zudem keine große Oberfläche, die der Unterdruck zum Niederhalten nutzen kann. Daher lassen sie sich leicht versehentlich verdrehen.&lt;/p&gt;&#10;&lt;p&gt;Am einfachsten zu bearbeiten sind daher runde oder quadratische Werkstücke, zumal wenn sie nahe an ihrem geometrischen Mittelpunkt gefräst werden.&lt;/p&gt;&#10;&lt;h2 id="welche-vakuumpumpe-wird-nun-also-benötigt"&gt;Welche Vakuumpumpe wird nun also benötigt?&lt;/h2&gt;&#10;&lt;p&gt;Ok, fassen wir zusammen:&lt;/p&gt;&#10;&lt;p&gt;Es ist immer eine Kombination aus Werkstückmaterial (leicht/ schwer zu bearbeiten, niedrige/hohe Reibungskoeffizienten), -Größe (je mehr Oberfläche, desto einfacher sind hohe Klemmkräfte realisierbar), Bedürfnissen durch die Bearbeitung wie Durchfräsungen (Toleranz gegenüber Leckageströmen), Vakuumtischgröße- und Typ sowie Bearbeitungsparametern, die die Auswahl der Vakuumpumpe bestimmt.&lt;/p&gt;&#10;&lt;p&gt;Daher sollten ein paar Erfahrungswerte vorliegen und klare Wünsche formuliert werden können: Wie groß sind die Werkstücke typischerweise? Werden oft Teile aus Plattenmaterial ausgeschnitten? Welche Materialien sollen verarbeitet werden? Werden Kühlschmierstoffe benötigt? Muss mit aggressiven Parametern vorgegangen werden, um Fertigungszeiten niedrig zu halten?&lt;/p&gt;&#10;&lt;h3 id="ein-paar-vorschläge-meinerseits"&gt;Ein paar Vorschläge meinerseits:&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Aluminium, kleine Werkstückgröße, keine Durchfräsungen: Kleinvolumige Kolben- oder Drehschieberpumpe mit einem Lochrastertisch&lt;/li&gt;&#10;&lt;li&gt;Holz und MDF, große Werkstücke, Nesting und eine Menge Durchfräsungen: Radialgebläse oder einstufiger Seitenkanalverdichter, Vakuumtisch mit poröser Oberfläche oder Rastervakuumtisch mit Vakuumvlies&lt;/li&gt;&#10;&lt;li&gt;Hochdrucklaminate, kleine Werkstücke, viele Durchfräsungen: Drehschieberpumpe mit großem Volumenstrom oder mehrstufige Seitenkanalgebläse sowie ein Lochrastervakuumtisch. Zudem Verwendung von an das Werkstück angepassten, speziellen Frässtrategien im CAM: späte Durchfräsungen in Kombination mit geringerer Tiefenzustellung.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="meine-wahl"&gt;Meine Wahl&lt;/h3&gt;&#10;&lt;p&gt;Da ich auf meiner Maschine Platten mit &lt;code&gt;60x60cm&lt;/code&gt; Größe in einem Rutsch bearbeiten möchte, habe ich mich für einen &lt;a href="https://blog.schallbert.de/why-vacuum-table/#meine-wahl"&gt;recht großen Lochrastervakuumtisch&lt;/a&gt; (&lt;code&gt;730 x 655 mm&lt;/code&gt;) mit &lt;code&gt;0.5mm&lt;/code&gt; Durchgangsbohrungen in einem &lt;code&gt;10mm&lt;/code&gt; Raster entschieden. Da ich noch immer eine Menge verschiedener Materialien auf meiner Maschine ausprobiere und hauptsächlich Prototypen anfertige, sehen meine Werkstücke selten ähnlich aus und haben sehr unterschiedliche Abmessungen (kleinstes Werkstück bis dato: &lt;code&gt;14x26mm&lt;/code&gt;). Daher musste ich in den sauren Apfel beißen und einen Haufen Geld in eine großzügig dimensionierte, trockenlaufende Drehschieberpumpe investieren, die für kleine Werkstücke einen hohen Unterdruck zur Verfügung stellt und dennoch eine Menge nicht abgedeckter Sauglöcher toleriert, ohne in die Knie zu gehen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_selection.jpg" alt="Image: Grid vacuum table" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Vergleich mehrerer Drehschieberpumpen (Bild bereitgestellt von Stritzelberger GmbH)&lt;/span&gt;&lt;a href="https://www.vakuumtisch.de/" class="attr-link" aria-label="Attribution 1"&gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die Pumpe stellt einen Volumenstrom von &lt;code&gt;40m³/h&lt;/code&gt; zur Verfügung und kann maximal &lt;code&gt;880mBar&lt;/code&gt; unter Umgebungsluftdruck gefahren werden. Sie wiegt in etwa &lt;code&gt;40kg&lt;/code&gt; und wird von einem &lt;a href="https://en.wikipedia.org/wiki/Induction_motor" target="_blank" rel="noopener noreferrer" class="external-link"&gt;3-Phasen Asynchronmaschine&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; mit einer Nennleistung von &lt;code&gt;1.3kW&lt;/code&gt; angetrieben.&lt;/p&gt;&#10;&lt;p&gt;Durch einen Umbau läuft meine Pumpe auf nur einer Phase. Ich habe zudem einen Motorschutzschalter hinzugefügt, um die Maschine vor Überlast zu schützen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-28_my_dst40_pump.jpg" alt="Image: My DST40 dry-running vane-type vacuum pump" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="ist-die-pumpe-laut"&gt;Ist die Pumpe laut?&lt;/h3&gt;&#10;&lt;p&gt;Na ja, &lt;code&gt;68dB(A)&lt;/code&gt; aus einem Meter Abstand sind nicht so richtig leise. Das Geräusch erinnert mich an das Außengerät einer Klimaanlage, die bei voller Last läuft. Ist die Fräsmaschine allerdings in Betrieb, so geht das Geräusch der Pumpe in den Fräsgeräuschen praktisch unter.&lt;/p&gt;&#10;&lt;p&gt;Aber überzeugen Sie sich selbst; ich habe ein kleines Video vom Betrieb der Pumpe erstellt:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/9CsPToFRYf3R7S6FC87gRZ" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Drehschiebervakuumpumpe: Inbetriebnahme&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Vakuumtisch für die CNC</title><link>https://blog.schallbert.de/why-vacuum-table/</link><pubDate>Fri, 21 Oct 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/why-vacuum-table/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_table_gr_thumb.jpg"&#10; class="post-cover"&#10; alt="Image of my hole grid vacuum table"&#10; title="Vakuumtisch für die CNC" /&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_table_operating_principle.jpg" alt="Image: Vacuum table principle of operation - pressure difference causes downforce" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Nehmen wir mal an, Sie sind nicht durch Zufall beim Surfen durch das Internet auf diesen Beitrag gestoßen. Sie wissen also bereits was ein Vakuumtisch ist (falls nicht: &lt;a href="https://en.wikipedia.org/wiki/Vacuum_table" target="_blank" rel="noopener noreferrer" class="external-link"&gt;hier weiterlesen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;). Sein Wirkprinzip wird im Bild auf der rechten Seite erläutert. Wahrscheinlich wissen Sie außerdem bereits, dass es verschiedene Typen Vakuumtische für unterschiedliche Zwecke gibt, und vielleicht auch schon, dass Sie einen anschaffen möchten.&lt;/p&gt;&#10;&lt;p&gt;Hervorragend! Dieser Artikel beschreibt sowohl meinen eigenen Weg hin zu einem Vakuumtisch, soll aber ebenfalls als Entscheidungshilfe für Sie dienen. Beginnen wir mit einem kleinen Interview.&lt;/p&gt;&#10;&lt;h2 id="warum-möchten-sie-einen-vakuumtisch-haben"&gt;Warum möchten Sie einen Vakuumtisch haben?&lt;/h2&gt;&#10;&lt;p&gt;Gleich zu Anfang die wichtigste Frage.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;🤩 Weil er toll aussieht, eine Menge Gewicht auf die Waage bringt, stabil und teuer ist?&lt;/li&gt;&#10;&lt;li&gt;😪 Weil Sie keine Lust mehr haben, sich ständig über das richtige Klemmen des Werkstücks Gedanken zu machen?&lt;/li&gt;&#10;&lt;li&gt;😖 Oder weil Sie bereits ein paar Fräser verschlissen haben beim Versuch durch Klemmen zu fahren?&lt;/li&gt;&#10;&lt;li&gt;😨 Oder doch weil sich Doppelklebeband als nicht stark genug erwiesen hat und Ihr Werkstück fliegen ging?&lt;/li&gt;&#10;&lt;li&gt;😎 Professionalisieren Sie Ihr Gewerk vielleicht gerade und Sie versuchen, Produktionszeit einzusparen?&lt;/li&gt;&#10;&lt;li&gt;😕 Vielleicht ist auch die Qualität Ihrer Werkstücke nach Bearbeitung unzureichend oder Sie haben Probleme, Plattenware plan zu bekommen?&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="meine-gründe-qualität-und-höhere-fertigungsgeschwindigkeit"&gt;Meine Gründe: Qualität und höhere Fertigungsgeschwindigkeit&lt;/h3&gt;&#10;&lt;p&gt;Dies hier soll mein Vakuumtisch in den Griff kriegen:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Bei Gravuren machen nicht vollkommen plan aufliegende Werkstücke mir einen Strich durch die Rechnung, da die Gravurbreite ungleichmäßig wird. Dies ist leider nicht nur auf Holzwerkstoffe beschränkt.&lt;/li&gt;&#10;&lt;li&gt;Momentan arbeite ich mit Anbindungen, um Frästeile in Position zu halten. Meine Frästeile benötigen also manuelle Nacharbeit und die Anbindungen am Werkstück können danach immer noch erahnt werden.&lt;/li&gt;&#10;&lt;li&gt;Wenn ich keine Anbindungen wünsche, muss ich eine &amp;ldquo;Zwiebelhaut&amp;rdquo; übrig lassen, kann also nicht ganz durchfräsen. Das macht die Bearbeitung der Rückseite z.B. mit einem Gravurfräser erforderlich, was durch die genaue Positioniererfordernisse viel Zeit in Anspruch nimmt.&lt;/li&gt;&#10;&lt;li&gt;Mit einem Vakuumtisch werden ballige Werkstücke für die Bearbeitung plan gezogen, sodass Probleme ungleicher Gravuren, nicht erwünschter Durchfräsungen usw. behoben sind.&lt;/li&gt;&#10;&lt;li&gt;Unter bestimmten Bedingungen &lt;a href="https://blog.schallbert.de/cnc-vibrates/"&gt;leidet Meine Maschine an Vibrationen&lt;/a&gt;, besonders bei Verwendung langer Zweizahnfräser bei großer Tiefenzustellung und größeren Geschwindigkeiten. Das zusätzliche Gewicht und die flächige Kraftverteilung durch den Vakuumtisch könnten dieses Problem lösen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="welcher-typ-vakuumtisch"&gt;Welcher Typ Vakuumtisch?&lt;/h2&gt;&#10;&lt;p&gt;Ok, nun sollten wir herausfinden, welche Art Vakuumtisch sich am besten für Ihren Einsatz und für Ihre Randbedingungen eignet. Vakuumtische sind prinzipbedingt nur dann sinnvoll, wenn Plattenmaterial mit entsprechend großer Oberfläche bearbeitet wird. Zudem sollte das Plattenmaterial nicht zu biegsam oder sehr porös sein - Folien, Gummimatten und Weichschäume beispielsweise können von einem Vakuumtisch kaum gehalten werden, da durch die Fräskräfte angehobene Kanten des Plattenmaterials sofort das Vakuum kollabieren lassen.&lt;/p&gt;&#10;&lt;p&gt;Es gibt drei Typen Vakuumtische:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Rastervakuumtische&lt;/li&gt;&#10;&lt;li&gt;Lochrastervakuumtische&lt;/li&gt;&#10;&lt;li&gt;Vakuumtische mit poröser Oberfläche&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="rastervakuumtisch"&gt;Rastervakuumtisch&lt;/h3&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_table_r.jpg" alt="Image: Ein Rastervakuumtisch. Zu erkennen ist eine Aluminiumplatte, durchzogen von Nuten. Diese sind in einem quadratischen Raster angeordnet. In diese Nuten lässt sich eine Gummischnur einlegen, welche die zu vakuumierende Geometrie festlegt." /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Ein Rastervakuumtisch (Bild zur Verfügung gestellt von Stritzelberger GmbH)&lt;/span&gt;&lt;a href="https://www.vakuumtisch.de/Grid-vacuum-table-R-Series" class="attr-link" aria-label="Attribution 1"&gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Viele Rastervakuumtische haben nur einen Vakuumanschluss, der in der Mitte des Tisches platziert ist. Der Bereich, in dem Unterdruck das Werkstück halten soll, wird mittels einer in das Raster gelegten (Schaum-) Gummidichtung bestimmt. Hierbei muss sich die Dichtung stets innerhalb des Werkstückes befinden.&lt;/p&gt;&#10;&lt;p&gt;Da die Dichtung bei Einschalten des Vakuums zusammengepresst wird und damit Vakuumtisch und Werkstück luftdicht miteinander verbindet, muss die Pumpe nur einen geringen Volumenstrom zur Verfügung stellen. Zudem hält das Werkstück bei entsprechend starkem Unterdruck sehr großen Zug- und Scherkräften stand, ohne sich zu lösen.&#10;Durch die flexible Dichtung können auch nicht ganz plan aufliegende Werkstücke oder solche mit rauen Oberflächen auf dieser Art Tisch gut bearbeitet werden.&lt;/p&gt;&#10;&lt;p&gt;Bei all den Vorteilen haben sie aber zwei gewichtige Nachteile: Werden erstens verschieden große Werkstücke verarbeitet, ist die Verlegung der Dichtung stets anzupassen. Außerdem muss beachtet werden, dass durch das meist in der Mitte befindliche Absaugloch auch die Werkstücke zentriert werden müssen; Anschläge für das vereinfachte Kantenfinden sind daher oft nicht verwendbar.&lt;/p&gt;&#10;&lt;p&gt;Zweitens darf nicht &amp;ldquo;innerhalb der Dichtung&amp;rdquo; durchgefräst werden. Tut man es doch, wird das Vakuum sofort zusammenbrechen, da die Pumpe plötzlich einen riesigen Luftstrom bewältigen muss. Daher darf das Werkstück nicht zu klein werden (das Raster ist das Maß der Dinge) oder man muss zusätzlich Arbeit in Klemmen oder Halterungen stecken, die dieses Problem lösen.&lt;/p&gt;&#10;&lt;p&gt;Kurzgesagt:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Rastervakuumtische funktionieren sehr gut, solange man nicht durchfräst.&lt;/li&gt;&#10;&lt;li&gt;Rastervakuumtische stellen geringe Anforderungen an die Pumpe und halten auch unebene oder verzogene Werkstücke sicher.&lt;/li&gt;&#10;&lt;li&gt;Rastervakuumtische sind günstig, benötigen aber bei unterschiedlichen Werkstückgrößen, kleinen Werkstücken oder Durchfräsungen extra angepasste Halterungen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="lochrastervakuumtisch"&gt;Lochrastervakuumtisch&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_table_seal.jpg" alt="Image: Hole grid vacuum table" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Ein Lochrastervakuumtisch (Bild zur Verfügung gestellt von Stritzelberger GmbH)&lt;/span&gt;&lt;a href="https://www.vakuumtisch.de/Rastervakuumtisch-R-Serie" class="attr-link" aria-label="Attribution 2"&gt;&#10; &lt;sup class="attr-id"&gt;[2]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Lochrastervakuumtische verfügen über - wie der Name schon sagt - in einem Raster angeordnete Sacklöcher, über die Luft abgesaugt wird. In jedem dieses Sacklöcher befindet sich eine winzige Durchgangsbohrung, die zu einem im Tisch befindlichen Kanalsystem führt. Der Durchmesser der Durchgangsbohrung wird vom vorrangig zu bearbeitenden Material vorgegeben - sie sind etwas größer bei &amp;ldquo;porösen&amp;rdquo; Materialien wie Holz und MDF, und kleiner für die Aluminium- oder Stahlbearbeitung.&lt;/p&gt;&#10;&lt;p&gt;Zumeist sind sie in zuschaltbare Segmente unterteilt, sodass bei kleineren Werkstücken nicht der ganze Tisch abgedeckt werden muss um Leckströme zu vermeiden. Dennoch ist es sinnvoll, weiterhin freiliegende Löcher abzudecken und damit den Vakuumdruck zu erhöhen.&lt;/p&gt;&#10;&lt;p&gt;Diese Vakuumtische bestehen meist aus drei Teilen: Dem Unterbau zur Montage auf dem Maschinenbett, einer mittleren Ebene mit Vakuumanschlüssen und dem Kanalsystem sowie einer oberen Ebene mit Lochraster, was zudem oft eine Bearbeitungsreserve zum Planfräsen aufweist.&lt;/p&gt;&#10;&lt;p&gt;Sie sind in der Lage, Werkstücke jeglicher Form niederzuhalten, vorausgesetzt sie besitzen eine ebene Fläche auf der Unterseite. Die kleinen Durchgangsbohrungen ermöglichen auch das Durchfräsen: die betroffenen Löcher liefern zwar keinen Beitrag mehr zur Klemmkraft, überfordern mit dem geringen Leckstrom aber auch die Vakuumpumpe nicht, sodass sie den Unterdruck bei den anderen Löchern aufrecht erhalten kann (solange man es mit der Anzahl freiliegender Löcher nicht übertreibt). Das Ausrichten des Werkstücks ist auf solchen Tischen ebenfalls einfach, da das Lochraster einfach mit Zentrierstiften bestückt werden kann, die einen Anschlag o.Ä. bilden.&lt;/p&gt;&#10;&lt;p&gt;Nachteile dieses Tischtyps sind ihr hoher Preis durch das aufwändige Design, mehr benötigtes Zubehör wie Vakuumverteiler bei einem Mehrkammersystem (und alles muss leckfrei gehalten werden), sowie hohe Anforderungen an die Vakuumpumpe bei Durchfräsungen.&lt;/p&gt;&#10;&lt;p&gt;Kurzgesagt:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Lochrastervakuumtische sind für jedes feste Werkstück mit genügend Oberfläche und glatter Unterseite geeignet.&lt;/li&gt;&#10;&lt;li&gt;Lochrastervakuumtische tolerieren Durchfräsungen und freiliegende Löcher, erfordern dann aber großzügig dimensionierte Vakuumpumpen.&lt;/li&gt;&#10;&lt;li&gt;Lochrastervakuumtische vereinfachen die Positionierung von Werkstücken ungemein.&lt;/li&gt;&#10;&lt;li&gt;Lochrastervakuumtische sind komplex in der Fertigung und daher vergleichsweise teuer.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="vakuumtische-mit-poröser-oberfläche"&gt;Vakuumtische mit poröser Oberfläche&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_table_porous.jpg" alt="Image: Porous surface table, MDF top layer" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Ein Porenvakuumtisch (Bild zur Verfügung gestellt von &amp;#39;Jens´ Vakuumtische&amp;#39;)&lt;/span&gt;&lt;a href="https://hobbyline.info/forum/index.php?thread/366-neue-erkenntnisse-von-meinem-vakuumtisch/&amp;amp;pageNo=1" class="attr-link" aria-label="Attribution 3"&gt;&#10; &lt;sup class="attr-id"&gt;[3]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Diese Art Vakuumtisch benötigt weder sichtbare Löcher noch Kanäle an seiner Oberfläche: Stattdessen ist die Oberfläche selbst luftdurchlässig. Für das Fräsen von Leiterplatten wird als Material oft Aluminiumschaum verwendet. Im Hobbybereich gibt es auch gute Erfahrungen mit überplanter mitteldichter Faserplatte. Bei Letzterer ist es beim Durchfräsen nicht einmal schlimm, wenn man sich mit der Materialdicke vertan hat, da MDF solche Fehler gut verzeiht und bei Bedarf einfach eine frische Platte aufgelegt werden kann.&lt;/p&gt;&#10;&lt;p&gt;Die Tische sind leicht und - mit viel Hintergrundwissen über Werkstoffe und Dünwwandfräsen - sogar als &amp;ldquo;Marke Eigenbau&amp;rdquo; zu fertigen. Im Netz gibt es dazu viele Beispiele.&lt;/p&gt;&#10;&lt;p&gt;Sie funktionieren am besten über einen großen Volumenstrom und arbeiten daher meist mit geringen Unterdrücken - sie erhalten ihre Klemmkraft also über die Fläche. Besser noch als Lochrastervakuumtische sind sie also für Durchfräsungen geeignet. Zudem sind sie in der Anschaffung günstig und können sogar mit Staubsaugern betrieben werden, deren Motoren auch ohne Förderstrom durch ein eigenes Gebläse gekühlt werden.&lt;/p&gt;&#10;&lt;p&gt;Ihre Nachteile: Durch ihr geringes Gewicht sind sie anfälliger für Vibrationen. Sie benötigen Werkstücke mit eher großer Oberfläche um adäquate Klemmkräfte zu realisieren und bei hohen Unterdrücken bleibt das poröse Obermaterial nicht maßhaltig, es &amp;ldquo;schrumpft&amp;rdquo; also bei aufgelegtem Werkstück etwas. Außerdem sind sie nicht geeignet bei Verwendung von Kühlschmierstoffen und haben einen ungünstigen statischen Reibungskoeffizienten (Je nach Werkstückpaarung bieten sie keinen besonders guten Halt gegen laterale Verschiebung).&lt;/p&gt;&#10;&lt;p&gt;Kurzgesagt:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Porenvakuumtische sind günstig zu erwerben und zu betreiben.&lt;/li&gt;&#10;&lt;li&gt;Porenvakuumtische sind für größere Werkstücke und Materialien, die keine Kühlschmierstoffe benötigen, gut geeignet.&lt;/li&gt;&#10;&lt;li&gt;Porenvakuumtische sind bei Durchfräsungen sehr gut geeignet.&lt;/li&gt;&#10;&lt;li&gt;Porenvakuumtische sind bei Verwendung von MDF nicht präzise in Z-Richtung.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="was-alle-diese-vakuumtische-gemeinsam-haben"&gt;Was alle diese Vakuumtische gemeinsam haben&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_table_vacuum_principle.jpg" alt="Image: How surface irregularities help keep pressure differences homogenous" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Die Klemmkraft auf das Werkstück in Z-Richtung kann für einen gegebenen Unterdruck auf identische Art berechnet werden. Aber vorsicht, dieser Unterdruck ist bei Durchfräsungen und anderen Quellen für Leckagen am Werkstück geringer als auf dem Manometer angezeigt.&lt;/p&gt;&#10;&lt;p&gt;Werden keine Durchfräsungen gefertigt, lässt sich die volle Werkstückoberfläche zur Berechnung der Klemmkraft hinzuziehen (und nicht nur die Summe der Fläche aller Löcher im Bereich unter dem Werkstück im Falle des Lochrastertisches). Das kommt daher, dass mikroskopisch kleine Unebenheiten auf der Oberfläche von Werkstück und Tisch einen geringen lateralen Luftfluss an der Grenzschicht zulassen.&lt;/p&gt;&#10;&lt;p&gt;Andererseits gilt dies auch für die Ränder des Werkstücks. Nutzt man also keinen Rastervakuumtisch, wird es immer Leckageluftströme geben. Fräst man zusätzlich komplett durch das Material, werden weitere Randzonen erzeugt, die wiederum geringere Klemmkräfte aufweisen als der Rest des Werkstücks.&lt;/p&gt;&#10;&lt;h2 id="welches-zubehör-für-einen-vakuumtisch"&gt;Welches Zubehör für einen Vakuumtisch?&lt;/h2&gt;&#10;&lt;p&gt;Ohne Pumpe funktioniert der Vakuumtisch natürlich gar nicht. Und obacht, werden hohe Unterdrücke und gleichzeitig große Volumenströme verlangt, bewegt sich der Preis für die gewünschte Pumpe ganz schnell in derselben Liga wie der Vakuumtisch. Aber das führt hier zu weit - ich habe den &lt;a href="https://blog.schallbert.de/cnc-vacuum-pumps/"&gt;Vakuumpumpen einen eigenen Artikel&lt;/a&gt; gewidmet. Aber es wird weiteres Zubehör benötigt:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Schrauben, Unterlegscheiben, Nutensteine / Muttern um den Tisch auf der Maschine zu befestigen&lt;/li&gt;&#10;&lt;li&gt;Sortiment verschieden dicker Unterlagsbleche für die plane Montage des Tisches gegenüber der Z-Achse der Maschine&lt;/li&gt;&#10;&lt;li&gt;Einen Vakuumverteiler, falls der Vakuumtisch mehrere Zonen besitzt&lt;/li&gt;&#10;&lt;li&gt;Vakuumleitungen&lt;/li&gt;&#10;&lt;li&gt;Einen Schalter mit Motorschutz für die Vakuumpumpe&lt;/li&gt;&#10;&lt;li&gt;gegebenenfalls ein Vakuumvlies (Holz-, Kunststoffbearbeitung) oder eine Lochrastermatte aus Gummi (Alu, Stahl)&lt;/li&gt;&#10;&lt;li&gt;Dichtband (für Rastervakuumtische)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="zusammenfassung"&gt;Zusammenfassung&lt;/h3&gt;&#10;&lt;p&gt;Alles in allem will die Anschaffung eines Vakuumtisches gut überlegt sein - gerade wegen des großen Kosten- und Zeitaufwandes für Montage, Einrichtung und Betrieb des Tisches. Entscheidet man sich dennoch dafür, so wird man bei zweckmäßigem Einsatz mit höherer Fertigungsqualität durch die flächige Auflage des Werkstücks, kürzerer Richtzeit durch Wegfallen der Klemmen und einfacherer Jobplanung belohnt - denn Materialanbindungen sind nun passé.&lt;/p&gt;&#10;&lt;h2 id="meine-wahl"&gt;Meine Wahl&lt;/h2&gt;&#10;&lt;p&gt;Ich arbeite ausschließlich mit Plattenmaterial. Ich fertige bisher Produkte aus Holz, Hochdrucklaminaten, Acrylglas, mitteldichter Faserplatte oder Aluminium-Verbundmaterial. Zumeist muss ich Konturen aus einer größeren Platte ausschneiden (Durchfräsen) und wende Nestingverfahren an, um möglichst wenig Abfall zu erzeugen. Ich arbeite oft zweiseitig, was eine genaue Lokalisierung des Werkstückes auf dem Maschinentisch erfordert. Daher kommt ein Rastervakuumtisch für mich nicht infrage.&lt;/p&gt;&#10;&lt;p&gt;Um die Kanten auf der Werkstückunterseite anzufasen, schneide ich im ersten Arbeitsgang meist nicht ganz durch, sondern lasse einen V-Fräser diesen letzten Schritt auf der Unterseite erledigen. Da ich hierfür eine sehr exakte Bestimmung der Werkstückhöhe benötige und der Unterdruck sich bei Auffräsen einer Kontur durch den Fasenfräser verringert, muss die Oberfläche des Vakuumtisches starr sein - daher kommt ein Porenvakuumtisch mit Oberfläche aus MDF für mich nicht in Frage.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_bl0607_dwg.jpg" alt="Image: Creating a drawing of my machine bed for the vacuum table manufacturer" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Stattdessen habe ich einen Hersteller für Vakuumtische direkt kontaktiert, um ein paar kleine Lochrastervakuumtische zu kaufen, die ich unabhängig voneinander einschalten und damit je nach Größe des Werkstücks Segmente auf meiner CNC mit Unterdruck versorgen könnte. Dieser meinte, dass die einfachere und bessere Lösung ein für meine Maschine angepasster Vakuumtisch sei, da bei Zusammenstellen mehrerer Tische Höhenunterschiede und Undichtigkeiten entstehen würden und die Zuführung der Vakuumleitung erschwert würde.&lt;/p&gt;&#10;&lt;p&gt;Also habe ich gespart und mir den teureren, großen Tisch zugelegt. Hierfür habe ich dem Hersteller Zeichnungsdaten meiner Maschine zur Verfügung gestellt und nach ein paar Gesprächen und Vorschlägen seitens des Herstellers hatte ich dann einen an meine Bedarfe angepassten Entwurf.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_custom_table.jpg" alt="Image: Custom designed vacuum table to fit my machine" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="lieferung-des-tisches"&gt;Lieferung des Tisches&lt;/h3&gt;&#10;&lt;p&gt;Der von mir ausgewählte Hersteller sitzt nicht zu weit von meinem Wohnort entfernt. Also bin ich hingefahren für eine kleine Führung durch die Produktion und konnte den Tisch dabei direkt mitnehmen. Da ich für die Montage meine alte Werkstückunterlage vom Maschinentisch nehmen musste, konnte ich in einem Zug direkt Schalldämmung im Maschinenrahmen anbringen. So wird die Maschine nochmal etwas leiser.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_table_mounting.jpg" alt="Image: Vacuum table mounting" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Danach habe ich den Vakuumtisch mit M6 Maschinenschrauben und T-Nut Muttern am Rahmen befestigt. Für eine ebene Oberfläche (gemessene Abweichung in Z über die gesamte Fläche des Maschinentisches unter &lt;code&gt;15/100mm&lt;/code&gt;, Messuhr siehe Bild unten links) habe ich Unterlegbleche verwendet und stundenlang nachkontrolliert und -justiert, bis ich zufrieden war.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/8qP4FeBN2RYAVxkB4SWPHi" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Parallelität der Z-Achse prüfen&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Für noch bessere Ergebnisse könnte man:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Den Vakuumtisch einmal komplett planfräsen&lt;/li&gt;&#10;&lt;li&gt;Dn Tisch wieder herunternehmen und den Maschinenrahmen so nachrustieren, dass die Profile exakt waagerecht aufliegen und die Oberseiten aller Rahmenteile auf das Hundertstel genau auf einer Ebene enden.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Dies erschien mir als unverhältnismäßig großer Aufwand angesichts der vorhandenen Maschinentoleranzen (Immerhin handelt es sich hier um einen Eigenbau), also gebe ich mich mit den oben erreichten Werten zufrieden.&lt;/p&gt;&#10;&lt;h3 id="anschluss-des-vakuumverteilers"&gt;Anschluss des Vakuumverteilers&lt;/h3&gt;&#10;&lt;p&gt;Als ich einfach drauflos die dünnen Vakuumleitungen zur Verbindung von Tisch und Verteile zurechtschnitt, dachte ich mir: &amp;ldquo;Warte mal eine Sekunde. Wäre es nicht klüger, die 9mm-Leitungen so kurz wie möglich zu halten, und lieber die 18er-Leitung zur Pumpe länger zu lassen und so den Druckabfall zu reduzieren?&amp;rdquo; Also habe ich mich erst einmal mit dem Taschenrechner zurückgezogen.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Mein Tisch hat etwa 4900 Sauglöcher mit je &lt;code&gt;0.5mm&lt;/code&gt; Durchmesser. Ihre zusammengerechnete Oberfläche beträgt \(A_{table}=962mm^2\), was in etwa einem Loch mit &lt;code&gt;35mm&lt;/code&gt; Durchmesser entspricht (ich ignoriere hier komplett die Widerstände an den Lochrändern.)&lt;/li&gt;&#10;&lt;li&gt;Die sieben Vakuumleitungen zum Verteiler haben einen Durchmesser von &lt;code&gt;9mm&lt;/code&gt;. Zusammengenommen liegt ihre Oberfläche bei \(A_{hose}=445mm^2\), entsprechen also einem Loch mit &lt;code&gt;24mm&lt;/code&gt; Durchmesser; deutlich weniger also als der Gesamtquerschnitt der Löcher des Tisches.&lt;/li&gt;&#10;&lt;li&gt;Für den Saugschlauch mit &lt;code&gt;18mm&lt;/code&gt; Innendurchmesser sieht es sogar noch schlimmer aus: \(A_{pump}=254mm\) liegt bei unter 30% des Querschnitts auf dem Tisch.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Wenn ich jetzt also von einem gleichmäßigen Leckagestrom über die sieben Segmente meines Tisches ausgehe, macht es Sinn, die Pumpenzuleitung so kurz wie möglich zu wählen, da sie bei vielen offenen Sauglöchern die höchste Flussgeschwindigkeit und damit den höchsten Druckverlust aufweist. In einem Szenario mit weniger verwendeten Segmenten wird irgendwann die 9mm-Leitung ausschlaggebend.&lt;/p&gt;&#10;&lt;p&gt;Daher ist also zu beachten, dass das Manometer am Vakuumverteiler bei größeren Leckageluftströmen einen höheren Wert anzeigt, als am Tisch tatsächlich anliegt!&lt;/p&gt;&#10;&lt;p&gt;Da ich die Zahl freiliegender Löcher aber begrenzen werde, kann ich mir einfach eine komfortable Position für den Vakuumverteiler aussuchen und die Verlegung abschließen 😉. Übrigens ist das erste auf meinem neuen Vakuumtisch gefertigte Teil ein Halter für den Vakuumverteiler aus Holz - wie im Bild rechts zu erkennen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-10-21_table_mounted.jpg" alt="Image: Vacuum table mounted" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Estlcam: Inverse Gravuren</title><link>https://blog.schallbert.de/negative-carving-with-estlcam/</link><pubDate>Tue, 27 Sep 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/negative-carving-with-estlcam/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-09-27_carve_results-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: A negative carve in wood"&#10; title="Estlcam: Inverse Gravuren" /&gt;&#10;&lt;p&gt;Der Plan war, ein Holzschild mit meiner CNC zu fertigen. Es sollte jedoch invertiert sein. Das heißt, die Lettern sollten sich vom flachen Hintergrund abheben. Da es verschiedene Wege gibt, so etwas in Estlcam umzusetzen (ein sogenanntes Computer Aided Machining Programm, was aus Zeichnungsdaten Fräspfade und später Maschineninstruktionen erzeugen kann), aber nicht alle Wege leicht zu finden oder gut anwendbar sind, biete ich hier eine Schritt-für-Schritt Anleitung an.&lt;/p&gt;&#10;&lt;h3 id="1-den-rahmen-des-schildes-importieren"&gt;1. Den Rahmen des Schildes importieren&lt;/h3&gt;&#10;&lt;p&gt;Zuerst sollten Zeichnungsdaten des Rahmens von dem zu fertigenden Schild vorliegen, beispielsweise im &lt;code&gt;DXF&lt;/code&gt;-Format. Ich habe ein rechteckiges Schild mit abgerundeten Ecken und definierter Rahmenstärke in meinem CAD-Programm gezeichnet. Es gilt sicherzustellen, dass die Frästiefe später nicht die Breite des Rahmens überschreitet, ansonsten könnte die Fräsarbeit zu instabil werden.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-09-27_frame.jpg" alt="Image: Frame for my sign" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="2-den-text-hinzufügen"&gt;2. Den Text hinzufügen&lt;/h3&gt;&#10;&lt;p&gt;Jetzt kann der Inhalt des Schildes in Form von Text durch Tastendruck auf das &amp;ldquo;Carve&amp;rdquo;-Symbol hinzugefügt werden. Dabei ist es sinnvoll, die Breite der Textbox an die inneren Rahmengrenze anzupassen, sodass der Text stets hineinpasst. Man hat verschiedene Carve-Stile zur Auswahl. Für die hier dargestellte Fräsarbeit ist &lt;code&gt;Carve aussen&lt;/code&gt; zu wählen. Nun den Text eintippen, Schriftart selektieren und die Schrifthöhe so anpassen, dass es im Schild ein harmonisches Bild ergibt.&lt;/p&gt;&#10;&lt;p&gt;Abhängig vom zu verwendenden Fräser sollte dann die Frästiefe gewählt werden. Bei kleineren Winkeln der V-Nutfräsers ist die Frästiefe meist größer zu wählen als bei breiten 90°-Fräsern wie ich ihn verwende. Für mein &lt;code&gt;12mm&lt;/code&gt; starkes Plattenmaterial setze ich eine Frästiefe von &lt;code&gt;4.5mm&lt;/code&gt; an. Anschließend wird die maximale Fräsbreite festgelegt. Gemeint ist hier, wie breit die &amp;ldquo;Täler&amp;rdquo; zwischen den Buchstaben sein sollen. Da ich für den invertierten Carve eine &amp;ldquo;Talsohle&amp;rdquo; benötige, habe ich hier einen sehr großen Wert eingestellt. Zum Schluss dann sollte die Taste &lt;code&gt;Umrahmung auswählen&lt;/code&gt; geklickt werden. Hiermit ist die Fräsarbeit dann nach außen begrenzt.&lt;/p&gt;&#10;&lt;p&gt;Bitte dieses Bearbeitungsfenster vorerst geöffnet lassen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-09-27_carve_text.jpg" alt="Image: Settings for the text to carve" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="3-werkzeugauswahl"&gt;3. Werkzeugauswahl&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-09-27_carve_preview1.jpg" alt="Image: Carve preview 1" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Wählen Sie nun das V-Nutwerkzeug durch Klicken in der entsprechenden Zeile Ihrer Werkzeugliste für die Textfräsung aus. Stellen Sie sicher, dass das Häkchen bei &lt;code&gt;Textobjekt zerlegen&lt;/code&gt; im Fenster &amp;ldquo;Text Fräsen&amp;rdquo; nicht erscheint, denn nur dann können Sie weitere Anpassungen vornehmen. Schließen Sie nun dieses Fenster per Klick auf &lt;code&gt;OK&lt;/code&gt;. Nun wählen Sie &lt;code&gt;Vorschau&lt;/code&gt; aus, um sich einen Eindruck von der Fräsarbeit zu verschaffen. Sehen Sie die eng beisammenstehenden parallelen Linien rechts im Bild? Dies wird durch den Parameter &lt;code&gt;Räumzustellung&lt;/code&gt; aus der Werkzeugtabelle verursacht. Für saubere Arbeiten mit einem Gravurfräser sollte dieser Wert bei kleiner gleich &lt;code&gt;5%&lt;/code&gt; bleiben.&lt;/p&gt;&#10;&lt;p&gt;Hmm, so werden Sie noch keine befriedigenden Arbeitsergebnisse erhalten, und die Fräsarbeit wird lange dauern. Wir benötigen also zusätzlich ein paar Räumpfade mit einem normalen Schaftfräser.&lt;/p&gt;&#10;&lt;h3 id="4-räumen"&gt;4. Räumen&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-09-27_carve_clearing_tool.jpg" alt="Image: Carve preview 1" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Speichern Sie Ihr Projekt spätestens jetzt ab. Nach dem aktuellen Schritt kann der Text nicht mehr bearbeitet werden!&lt;/p&gt;&#10;&lt;p&gt;Um den Fräser für die Räumung der &amp;ldquo;Talsohle&amp;rdquo; hinzuzufügen, wählen Sie den Carve-Pfad aus und wählen &lt;code&gt;Textobjekt zerlegen&lt;/code&gt; an. Bestätigen Sie mit &lt;code&gt;OK&lt;/code&gt; und wählen den Fräspfad erneut aus. Nun erscheint die Werkzeugkonfiguration. Fügen Sie das Räumwerkzeug hinzu. Vorzugsweise ist sein Durchmesser geringer als der Abstand zwischen Schrift und Rahmen, sodass eine möglichst große Fläche später glatt geräumt werden kann (Anmerkung: Ich habe das nicht gemacht, weswegen auf meinem Beispiel ein paar raue Stellen übrig geblieben sind). Die Räumzustellung dieses Fräsers wähle ich bei Holz zwischen 20% und 40%.&lt;/p&gt;&#10;&lt;p&gt;Wenn Sie für die Gravur einen Fräser ohne Abflachung am Boden gewählt haben (ich empfehle hier gern &lt;code&gt;0.5mm&lt;/code&gt; als guten Kompromiss zwischen hoher Auflösung und guter Fertigungsqualität), können Sie die Räumzustellung des Fräsers in der Werkzeugtabelle zur Verbesserung der Oberflächenqualität noch einmal nach unten korrigieren.&lt;/p&gt;&#10;&lt;h3 id="5-zweite-vorschau"&gt;5. Zweite Vorschau&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-09-27_carve_preview2.jpg" alt="Image: Carve preview 2" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ok, jetzt sollten wir durch sein. Nochmal kurz die Vorschau ausgewählt und sich über die reduzierte Verarbeitungszeit gefreut, und ran an die Maschine?&lt;/p&gt;&#10;&lt;p&gt;Ah, eine weitere Optimierung kann für das Gravurfräsen angebracht sein: Unter &lt;code&gt;Einstellungen&lt;/code&gt; die Sicherheitshöhe auf ein Minimum reduzieren (aber ohne in Klemmen oder andere Hindernisse zu rauschen). Das ständige Aus- und Eintauchen ins Material kostet bei Gravuren in Estlcam immer recht viel Zeit, wenn sich die Sicherheitshöhe in großem Abstand zur Werkstückoberfläche befindet.&lt;/p&gt;&#10;&lt;p&gt;Keine Sorge, wenn die Schrift in der Vorschau nun nicht mehr lesbar ist. Das ist total normal, weil ja nur die (jetzt wegen Verwendung des Räumfräsers gröber dargestellten) Fräspfade angezeigt werden.&lt;/p&gt;&#10;&lt;p&gt;Nach einer letzten Prüfung von Vorschüben und Werkzeugen kann&amp;rsquo;s jetzt endlich losgehen :)&lt;/p&gt;&#10;&lt;h3 id="engrave"&gt;6. Auf die CNC damit!&lt;/h3&gt;&#10;&lt;p&gt;Viel Spaß beim Fräsen. Ich für meinen Teil fand es sehr entspannend, der Maschine dabei zuzusehen, wie sie langsam ein immer deutlicher werdendes Bild ins Holz schneidet, was aber erst bei Verwendung des V-Fräsers lesbar wird.&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-09-27_carve_results.jpg" alt="Image: The completed sign" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;</description></item><item><title>AnywhereAmps Alpha</title><link>https://blog.schallbert.de/anywhereamps-diy/</link><pubDate>Sat, 10 Sep 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/anywhereamps-diy/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/front-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: AnywhereAmps Alpha&amp;#39;s insides"&#10; title="AnywhereAmps Alpha" /&gt;&#10;&lt;h2 id="pläne"&gt;Pläne&lt;/h2&gt;&#10;&lt;p&gt;{% include gallery id=&amp;ldquo;gallery&amp;rdquo; caption=&amp;ldquo;Die von mir angefertigten und verwendeten Pläne für &lt;strong&gt;AnywhereAmps Alpha&lt;/strong&gt;. Zum Vergrößern anklicken.&amp;rdquo; %}&lt;/p&gt;&#10;&lt;h2 id="listen"&gt;Listen&lt;/h2&gt;&#10;&lt;h3 id="werkzeuge"&gt;Werkzeuge&lt;/h3&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Benötigte Tools&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Bohrmaschine&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Oberfräse, Zirkelhalterung&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Multitool: Schleifen, Holzschnitt, Fräskopf&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Hand- oder Tischkreissäge mit Schutzbügel&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Handsäge, fein&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lochschneider d&amp;gt;=100mm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Stechbeitel, 12mm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Hammer&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schraubendreher, Bits&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Holzbohrer: 3mm, 4, 5, 6, 8, 10, 12mm, Senkbohrer 45°, Forstner 30mm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Stahlbohrer: 4,3 mm, Senkung 45°&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Oberfräsere: 8mm gerade, 12mm gerade, 45°/d12mm optional&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lötkolben, Lötzinn, Schrumpfschlauch, Heißkleber, 2m 0,75mm² Kabel etc.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/wood_tools.jpg" alt="Holzschnitzwerkzeuge" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="materialliste"&gt;Materialliste&lt;/h3&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Artikel&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Größe [mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Anzahl&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;~ Preis [€]&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Kessel: Pappel Multiplex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12&amp;quot; x 6&amp;quot; x 7mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;62&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Ober-/Unterteil: Pappel-Multiplex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12&amp;quot; x 12mm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;8&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lautsprecher-Adapterplatten: Pappel Multiplex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;L 300 B 150 W 12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Verstärker-Halteplatte: Pappel Multiplex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;L 160 B 95 W 12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Verstärkung &amp;amp; Halterungen: Birke Multiplex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;L 220 B 30 W 12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schrauben (vorzugsweise Torx)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12 x 3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;20&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schrauben (Batteriehalter, Haltegriff)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;16 x 3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schrauben &amp;amp; Muttern, Leiterplattenhalter, Unterlegscheiben&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;15 x M4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;8&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schaumstoffdichtung&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2000 x 9 x 3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1/4&amp;quot; Eingangsbuchse&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;50 x 12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Netzschalter mit LED, 250VAC 4A&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Potentiometer 250k log 6mm Achse + Lautstärkeknopf&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;22mm Durchmesser&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;9V Ausgangsbuchse&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5.6 brl, 2.4pin&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lüftergitter (Lautsprecherschutzabdeckung)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;120 x 120&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&amp;ldquo;I-Type&amp;rdquo; Schaumstoffdichtung (Lautsprecher)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;600 x 9 x 2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Aluminium-Gehäuse&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;143 x 72 x 43&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Class-D Endstufenplatine, z.B. TPA 3110&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;70 x 100 + 22&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;16&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Piezo-Hochtöner, z. B. Monacor MPT-005&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;85 x 85 x 72&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&amp;quot; Tief-/Mitteltöner, z. B. Visaton KT100 V 4Ohm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;99 x 99 x 52&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;25&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Batterie, 10V - 18V&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Was Sie bevorzugen&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;?&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Elektronikteile für Platine, Sicherung&amp;hellip;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;10&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Ersatzholz z.B. als Maske, Schablone, Helfer&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;z.B. 300 x 300&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="bauteile-auf-der-platine"&gt;Bauteile auf der Platine&lt;/h3&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;ID&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Artikel&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Typ&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Wert&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;R1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Widerstand&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0207&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;100k&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;C1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Elko&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;RM=2 D=5&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1 µF&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;C2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Elko&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;RM=2 D=5&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4,7 µF&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;R3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Widerstand&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0207&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;10k&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;R2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Widerstand&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0207&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;100k&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Stiftleiste&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2-polig&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Stiftleiste&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4-polig&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Stiftleiste&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2-polig&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;C1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Elko&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;RM=2 D=5&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2,2 µF&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;R4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Widerstand&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0207&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;10k&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;IC1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;NE5532&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;DIP-8&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="brückendrähte"&gt;Brückendrähte&lt;/h3&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Von&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Nach&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Länge [mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(11/1)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(11/2)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2.54&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(10/3)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(10/1)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5.08&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(14/6)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(14/2)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;10.6&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(8/1)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(8/6)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12.7&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(9/2)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(9/5)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;7.62&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(3/2)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(3/0)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5.08&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(22/0)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;(22/1)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2.54&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Loch (x/y)&lt;/p&gt;&#10;&lt;h2 id="der-aufbau"&gt;Der Aufbau&lt;/h2&gt;&#10;&lt;h3 id="löcher-schneiden"&gt;Löcher schneiden&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/adapterboard_before.jpg" alt="Bild: Adapterplatte angezeichnet" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;strong&gt;Warnung&lt;/strong&gt; Wenn Sie den Lochschneider als Handwerkzeug verwenden, seien Sie sehr vorsichtig. &lt;em&gt;Ich halte es für ein gefährliches Werkzeug.&lt;/em&gt; Gehen Sie sanft vor, wählen eine relativ hohe Drehzahl &lt;code&gt;&amp;gt;300rpm&lt;/code&gt; und üben Sie nur geringem Druck aus. Verwenden Sie eine Drehmomentstütze und den Drehmomentbegrenzer Ihrer Bohrmaschine. Vergewissern Sie sich, dass die Schneidköpfe richtig befestigt sind und dass das Werkstück sicher befestigt ist. Bereiten Sie sich darauf vor, dass die Maschine plötzlich hängen bleiben kann und beachten Sie das hohe Trägheitsmoment des schweren Werkzeugs.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Bereiten Sie das Brett für die Lautsprecheradapterplatten vor wie &lt;a href="https://blog.schallbert.de/anywhereamps-diy/#pl%c3%a4ne"&gt;in den &amp;ldquo;Plänen&amp;rdquo;&lt;/a&gt; gezeigt.&lt;/li&gt;&#10;&lt;li&gt;Bereiten Sie das Gehäuse vor: Markieren Sie die Mitte der Höhe und zeichnen Sie eine Hilfslinie an der Außenseite. Eventuell sollte ein Maßband verwendet werden, um die Krümmung zu berücksichtigen. Markieren Sie dann die drei Lochmitten. Ich schlage eine geodätische Kurve von &lt;code&gt;125mm&lt;/code&gt; Radius zwischen ihnen vor.&lt;/li&gt;&#10;&lt;li&gt;Bereiten Sie den oberen Kreis für den Ausschnitt des Bedienfeldes vor.&lt;/li&gt;&#10;&lt;li&gt;Wähle einen Lochdurchmesser von &lt;code&gt;75mm&lt;/code&gt; auf dem Lochschneider.&lt;/li&gt;&#10;&lt;li&gt;Schneiden Sie zwei Löcher für die Hochtöner in die Adapterplatte. Wenn die Platine nach der Hälfte des Schnitts umgedreht wird, werden die Kanten der Löcher sauberer.&lt;/li&gt;&#10;&lt;li&gt;Schneiden Sie nun die beiden Löcher in das Gehäuse. Seien Sie hier sehr vorsichtig, Sie können die innere Oberfläche leicht zerstören, wenn Sie mit zu geringer Geschwindigkeit schneiden oder vertikalen Druck ausüben.&lt;/li&gt;&#10;&lt;li&gt;Schneiden Sie ein Loch in die von Ihnen vorbereitete obere Platte des Gehäuses. Ich habe &lt;code&gt;76mm&lt;/code&gt; verwendet.&lt;/li&gt;&#10;&lt;li&gt;Ändern Sie den Lochdurchmesser an Ihrem Cutter auf &lt;code&gt;92mm&lt;/code&gt; und wiederholen Sie den Vorgang für den Bass/Mitten-Lautsprecher.&lt;/li&gt;&#10;&lt;li&gt;Den Schnitt für das mittlere Loch des Gehäuses wiederholen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/shell_holes.jpg" alt="Bild: Löcher in das Gehäuse schneiden" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="fräsen"&gt;Fräsen&lt;/h3&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/router_topbottom.jpg" alt="Bild: Fräsen des Deckels per Zirkel" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Legn Sie den Kessel auf das Brett, aus dem Ober- und Unterteil des Gehäuses gefertig werden sollen. Zeichnen Sie Innen- und Außendurchmesser an.&lt;/li&gt;&#10;&lt;li&gt;Finden Sie den Mittelpunkt dieses Kreises, z.B. mit Hilfe der &lt;a href="https://www.wikihow.com/Find-the-Center-of-a-Circle" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Methode 3 dieser Anleitung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/li&gt;&#10;&lt;li&gt;Montieren Sie das Zirkelwerkzeug auf Ihrer Oberfräse und spannen Sie einen geraden Fräskopf mit &lt;code&gt;12mm&lt;/code&gt; Durchmesser ein.&lt;/li&gt;&#10;&lt;li&gt;Schneiden Sie den &amp;ldquo;inneren&amp;rdquo; Kreis für den Falz. Ich empfehle eine Tiefe von &lt;code&gt;4mm&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;(optional) Montieren Sie den &lt;code&gt;8mm&lt;/code&gt;-Fräskopf.&lt;/li&gt;&#10;&lt;li&gt;Schneiden Sie den &amp;ldquo;äußeren&amp;rdquo; Kreis in mehreren Schritten, bis Sie die gesamte Tiefe Ihres Brettes ausgeschnitten haben.&lt;/li&gt;&#10;&lt;li&gt;Drehen Sie den Kreis um, und fasen Sie die Kanten z.B. mit einem Radiusfräser an.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="schnitz--und-modellierarbeiten"&gt;Schnitz- und Modellierarbeiten&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/adapterboard_curve.jpg" alt="Bild: 3D-Schnitzerei der Adapterplatten" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Sowohl die Adapterplatten als auch die Batteriehalterung sitzen am Durchmesser des Gehäuses, so dass sie eine gebogene Form haben müssen. Mit Handwerkzeugen fand ich die Arbeiten recht anspruchsvoll.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Bestimmen Sie die benötigten Tiefen für die parallelen Schnitte, damit beim Verbinden der Schnitte eine gewölbte Oberfläche entsteht. Für die Adapterplatten nehmen Sie den inneren Schalendurchmesser, für die Akkuhalterung den äußeren. Beachten Sie, dass die Adapterplatten für die Lautsprecher konvex sind, während die Batteriehalterung konkav sein muss.&lt;/li&gt;&#10;&lt;li&gt;Verwenden Sie eine Säge, um diese parallelen Schnitte durchzuführen. Berechnen Sie dabei die Breite des Sägeblatts mit ein.&lt;/li&gt;&#10;&lt;li&gt;Verbinden Sie die Schnitte mit einem Stechbeitel oder Meißel. Versuchen Sie, entlang der Maserung zu arbeiten.&lt;/li&gt;&#10;&lt;li&gt;Verwenden Sie einen Bandschleifer, Multitool oder eine Raspel um die Wölbung zu glätten, bis sie sehr gut passt. Die Verbindung muss schließlich am Ende Luftdicht sein.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/adapterboard_done.jpg" alt="Bild: Schleifen der Adapterplatten" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;6. Kleben Sie die Adapterplatten mit Holzleim auf der Innenseite des Kessels fest.&#10;7. Vergewissern Sie sich, dass die Löcher gut ausgerichtet sind. Nehmen Sie dann Heißkleber oder eine Mischung aus Leim und Holzmehl zur Hand, um die Adapter zu fixieren und abzudichten, während der Leim aushärtet.&#10;8. Schleifen Sie den Anschluss zwischen den beiden Teilen an oder nehmen Sie eine Viertelstabfräser und bringen eine Rundung an. Ich fand, dass dies der am wenigsten kontrollierbare Teil des ganzen Baus war, weil die Oberfräse nicht richtig auf der gekrümmten Oberfläche sitzen möchte, was die Kanten unterschiedlich breit werden lässt. Ich musste mit dem Multitool einiges an Nacharbeit leisten.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/shell_chamfered.jpg" alt="Bild: Kessel mit Adapterplatten, abgeschrägt" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="weitere-holzarbeiten"&gt;Weitere Holzarbeiten&lt;/h3&gt;&#10;&lt;p&gt;Kopieren Sie die Abmessungen des Verstärkerträgers aus dem Abschnitt &lt;a href="https://blog.schallbert.de/anywhereamps-diy/#pl%c3%a4ne"&gt;Pläne&lt;/a&gt;. Verwenden Sie einen Forstnerbohrer oder den Lochfräser, um die Vertiefung für das Potentiometer zu fertigen. Eine Tiefe von &lt;code&gt;6mm&lt;/code&gt; sollte ausreichen, um das Potentiometer richtig zu montieren. Für den Schalter ist ein Stufenbohrer gut geeignet, sodass der benötigte Durchmesser genau erreicht werden kann. Bohren Sie schließlich die Löcher für die Buchsen.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/batterymount.jpg" alt="Bild: Handgefertigte Batteriehalterung" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Da es eine Vielzahl von Batterietypen gibt, habe ich kein spezielles Design für den Schieber und die Batteriekontakte entworfen. Ich habe einfach typische Kabelschuhe verwendet und sie in die Adapterplatte geklebt, damit sie einen guten elektrischen Kontakt zu den Polen der Batterie herstellen können. Da meine Batterie keine Verriegelung hat, habe ich den Schieber wie einen Keil allmählich dicker werden lassen, so dass die Batterie an der richtigen Stelle klemmen würde.&lt;/p&gt;&#10;&lt;p&gt;Um das Gehäuse oben und unten geschlossen zu halten und gleichzeitig die Holzoberfläche vor Stößen zu schützen, habe ich ein paar Holzstangen zugeschnitten. Sie sind &lt;code&gt;25mm&lt;/code&gt; länger als die Gesamthöhe des Gehäuses und ihre Breite liegt bei &lt;code&gt;25mm&lt;/code&gt; und &lt;code&gt;12mm&lt;/code&gt; an den Gehäuseseiten.&lt;/p&gt;&#10;&lt;h3 id="elektronik"&gt;Elektronik&lt;/h3&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/circuit_board_soldered.jpg" alt="Bild: fertig verlötete Leiterplatte" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Schneiden Sie die Platine auf die benötigte Größe. Fertigen Sie Bohrungen für die Schrauben nach &lt;a href="https://blog.schallbert.de/anywhereamps-diy/#pl%c3%a4ne"&gt;Plan&lt;/a&gt;. Es ist ratsam, das Layout maßstabsgetreu auszudrucken, damit Sie ihn als Maske verwenden können.&lt;/li&gt;&#10;&lt;li&gt;Platzieren Sie die Leiterbahnunterbrechungen. Man kann dafür einen &lt;code&gt;3mm&lt;/code&gt; Metallbohrer verwenden und von Hand vorgehen, falls kein spezielles Werkzeug dafür vorhanden ist.&lt;/li&gt;&#10;&lt;li&gt;Fertigen und platzieren Sie die Brückendrähte. Nehmen Sie gern diese &lt;a href="https://blog.schallbert.de/anywhereamps-diy/#br%c3%bcckendr%c3%a4hte"&gt;Liste&lt;/a&gt; als Referenz.&lt;/li&gt;&#10;&lt;li&gt;Platzieren Sie die Widerstände.&lt;/li&gt;&#10;&lt;li&gt;Verwenden Sie etwas Klebeband, um die Teile auf der Platine zu fixieren. Anschließend können Sie diesen Satz Teile verlöten.&lt;/li&gt;&#10;&lt;li&gt;Platzieren Sie die Kondensatoren. Denken Sie daran, dass Sie vielleicht nicht genug Platz in Ihrem Gehäuse haben, so dass Sie sie eventuell lieber waagerecht einbauen möchten.&lt;/li&gt;&#10;&lt;li&gt;Platzieren, fixieren und schließlich löten Sie nun die restlichen Bauteile.&lt;/li&gt;&#10;&lt;li&gt;Bereiten Sie alle Verbindungen und Drähte zur Platine vor: Potentiometer, Instrumenteneingang, Endstufenausgang, &amp;ldquo;VCC/GND&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Überprüfen Sie ob Stromversorgung funktioniert. Wenn nur &lt;code&gt;VCC/GND&lt;/code&gt; angeschlossen wird, sollte die Schaltung zwischen &lt;code&gt;2mA&lt;/code&gt; und &lt;code&gt;10mA&lt;/code&gt; Strom ziehen, je nach Potentiometerstellung und Versorgungsspannung. Achten Sie darauf, dass Sie die Polarität nicht verwechseln, der NE5532 wird dadurch zerstört (glauben Sie mir, ich weiß es!). Sie sollten &lt;code&gt;VCC/2&lt;/code&gt; messen, wenn Sie den Ausgang der Op-Amp-Stufe, die die virtuelle Masse erzeugt gegen Masse abgreifen.&lt;/li&gt;&#10;&lt;li&gt;Überprüfen Sie die Verstärkungsstufe. Wenn Sie ein Oszilloskop und einen Funktionsgenerator haben, sollte das eine einfach sein. Stehen solche Werkzeuge nicht zur Verfügung, schließen Sie den Eingang an eine Audioquelle an, z. B. ein Smartphone, und den Ausgang an einen &lt;code&gt;2k2&lt;/code&gt;-Widerstand in Reihe mit einer LED und einer Diode wie &lt;code&gt;1N4148&lt;/code&gt; oder &lt;code&gt;1N4007&lt;/code&gt;. Drehen Sie Ihr Smartphone auf volle Lautstärke. Die LED sollte anfangen zu flackern, wenn der Widerstand des Potentiometers vergrößert wird. Ab welcher Position das Flackern einsetzt, hängt von der Vorwärtsspannung der LED, der Versorgungsspannung und der maximalen Ausgangsspannung der Quelle ab.&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/ampcarrier.jpg" alt="Bild: Verstärkerträger und Elektronikgehäuse" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/li&gt;&#10;&lt;li&gt;Ich schlage vor, die gesamte Elektronik und den Audioeingang - mit Ausnahme des &amp;ldquo;9V&amp;rdquo;-Versorgungsreglers - in ein Metallgehäuse einzubauen, um elektromagnetische Störungen zu reduzieren. Je nach Leistung der Endstufe werden passive oder sogar aktive Kühlkörper benötigt. In meinem Fall genügen einige Löcher im Gehäuse.&lt;/li&gt;&#10;&lt;li&gt;Befestigen Sie das Gehäuse an der Adapterplatte der Endstufe.&lt;/li&gt;&#10;&lt;li&gt;Schneiden Sie Löcher in das Gehäuse, wie auf der Adapterplatte angegeben.&lt;/li&gt;&#10;&lt;li&gt;Schalter, Potentiometer und Buchsen einsetzen. Die Anschlussdrähte anlöten.&lt;/li&gt;&#10;&lt;li&gt;Fügen Sie eine Sicherung, z.B. &lt;code&gt;4.0A&lt;/code&gt;, in den Aufbau auf dem Minuspfad zur Batterie für zusätzliche Sicherheit ein.&lt;/li&gt;&#10;&lt;li&gt;Montieren Sie Vorverstärker und Endstufe.&lt;/li&gt;&#10;&lt;li&gt;Nun kommt der finale Test. Nehmen Sie dafür z.B. einen alten Lautsprecher. Ohne angeschlossene Quelle sollten Sie bei voller Verstärkung nur ein leichtes Rauschen aus dem Lautsprecher hören. Wenn Sie Ihr Smartphone anschließen, beginnen Sie mit einer Verstärkung von &amp;ldquo;0&amp;rdquo; und einer geringen Lautstärke des Smartphones. Erhöhen Sie dann die Verstärkung des Vorverstärkers, um zu prüfen, ob alles funktioniert.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="endmontage"&gt;Endmontage&lt;/h3&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/speakers_mounted.jpg" alt="Bild: Chassis im Gehäuse montiert" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Montieren Sie die Lautsprecher. Verwenden Sie Heißkleber oder eine Schaumstoff-/Gummidichtung, um die Verbindungen luftdicht zu machen.&lt;/li&gt;&#10;&lt;li&gt;Verschließen Sie alle noch vorhandenen Löcher.&lt;/li&gt;&#10;&lt;li&gt;Löten Sie die Hochtöner parallel zum Basslautsprecher und schließen Sie das Ausgangskabel Ihres Verstärkers an.&lt;/li&gt;&#10;&lt;li&gt;Montieren Sie den Batteriehalter und schließen Sie die Kabel an die Klemmen des Verstärkers an.&lt;/li&gt;&#10;&lt;li&gt;Bringen Sie eine Dichtung an der oberen und unteren Deckelplatte an um das Gehäuse später luftdicht versiegeln zu können.&lt;/li&gt;&#10;&lt;li&gt;Drehen Sie die Baugruppe um und setzen Sie die &amp;ldquo;obere&amp;rdquo; Platte mit dem Verstärker wie auf dem Bild gezeigt ein.&lt;/li&gt;&#10;&lt;li&gt;Achten Sie darauf, dass keine der Lautsprecherklemmen das Verstärkergehäuse berührt, sonst besteht die Gefahr eines Kurzschlusses in der Endstufe.&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/aa_alpha/inside_thumb.jpg" alt="Bild: alle Komponenten an ihrem Platz" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/li&gt;&#10;&lt;li&gt;Die Simulation zeigt, dass die Basswiedergabe besser wird, wenn man Dämmwolle hinzufügt. Ich habe ein altes Kissen &amp;ldquo;geschlachtet&amp;rdquo;, um welche zu bekommen.&lt;/li&gt;&#10;&lt;li&gt;Schließen Sie das Gehäuse und befestigen Sie es mit den Holzstäben, die Sie zuvor vorbereitet haben (oder verwenden Sie stattdessen Schrauben)&lt;/li&gt;&#10;&lt;li&gt;Fertig, Applaus! Sieht AnywhereAmps Alpha nicht wunderschön aus?&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="feinschliff"&gt;Feinschliff&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Die Hochtöner sind zu laut? Prüfen Sie, wie viel höher die Empfindlichkeitswerte Ihrer Hochtöner im Vergleich zum Bass-Chassis sind. Löten Sie ggf. 1W-Widerstände in Reihe zu den Hochtönern. Laut den &lt;a href="https://blog.schallbert.de/projects/aa_alpha/#sound-hardware"&gt;technischen Daten der Chassis&lt;/a&gt; habe ich Werte von &lt;code&gt;96dB@1W/1m&lt;/code&gt; zu &lt;code&gt;80dB@1W/1m&lt;/code&gt;. Ich musste die Hochtöner also deutlich leiser machen. Mit mit einem &lt;code&gt;1k&lt;/code&gt;-Potentiometer in Reihe dämpfte ich die Hochtöner auf das von mir gewünschte Maß herunter und wählte schließlich einen Widerstand von &lt;code&gt;470Ohm&lt;/code&gt;, um das Potentiometer zu ersetzen.&lt;/li&gt;&#10;&lt;li&gt;Der Bass flattert bei hoher Lautstärke? Höchstwahrscheinlich ist das Gehäuse nicht luftdicht. Legen Sie einen Testton auf, z.B. &lt;code&gt;35Hz&lt;/code&gt; &lt;a href="https://www.youtube.com/watch?v=yvHCAne_VLo" target="_blank" rel="noopener noreferrer" class="external-link"&gt;youtube link&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, und prüfen Sie, ob Sie hören oder fühlen, wo die Luft durchkommt. Reparieren Sie das/die Leck(s).&lt;/li&gt;&#10;&lt;li&gt;Ihr 9-Volt-Schaltwandler strahlt Rauschen in Ihre Verstärkeranlage ab? Versuchen Sie, mit einem &lt;code&gt;100nF&lt;/code&gt;-Kondensator zu glätten, und fügen Sie einen &lt;code&gt;10k&lt;/code&gt;-Parallelwiderstand hinzu, um eine Dummy-Last zu erzeugen. Wenn beides nicht hilft, nehmen Sie stattdessen einen Linearwandler wie &lt;a href="https://www.elektronik-kompendium.de/public/schaerer/ureg3pin.htm" target="_blank" rel="noopener noreferrer" class="external-link"&gt;LM7809&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Da er viel mehr Wärme abgibt als sein schaltender Kollege, sollten Sie ihn an Ihrem Metallgehäuse montieren.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/projects/aa_alpha/"&gt;Zurück zur Projekt-Hauptseite&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>CNC job erstellen</title><link>https://blog.schallbert.de/cnc-job-setup/</link><pubDate>Mon, 22 Aug 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cnc-job-setup/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-22_cnc_job_setup-thumb.jpg"&#10; class="post-cover"&#10; alt="Image of a CNC job demo"&#10; title="CNC job erstellen" /&gt;&#10;&lt;h2 id="demo-video-über-vorbereitung-und-ausführung-eines-fräsjobs"&gt;Demo video über Vorbereitung und Ausführung eines Fräsjobs&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/mFreMUKxXktZH3kpqdENGJ" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;How-to: CNC job setup &amp;amp;amp; quick demo&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die folgenden Abschnitte beschreiben die im Video gezeigten Schritte.&lt;/p&gt;&#10;&lt;h3 id="checkliste-zur-vorbereitung"&gt;Checkliste zur Vorbereitung&lt;/h3&gt;&#10;&lt;p&gt;Bei komplett heruntergefahrener Anlage:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Das Werkstück sicher im Arbeitsbereich der CNC befestigen&lt;/li&gt;&#10;&lt;li&gt;Die G-Code Datei auf den Maschinenrechner übertragen&lt;/li&gt;&#10;&lt;li&gt;Not-Aus aktiviert (verhindert unbeabsichtigtes Anlaufen)&lt;/li&gt;&#10;&lt;li&gt;Energieversorgung zur CNC und Peripherie einschalten&lt;/li&gt;&#10;&lt;li&gt;Maschinenrechner mit der CNC verbinden und CNC-Programm starten&lt;/li&gt;&#10;&lt;li&gt;Not-Aus deaktivieren&lt;/li&gt;&#10;&lt;li&gt;Zu verwendende Fräser bereitlegen und auf Beschädigungen prüfen&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="checkliste-für-hochfahren-der-cnc"&gt;Checkliste für Hochfahren der CNC&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Not-Aus quittieren (Schrittmotortreiber aktivieren)&lt;/li&gt;&#10;&lt;li&gt;Maschine referenzieren&lt;/li&gt;&#10;&lt;li&gt;Werkzeugwechsel auf das erste Werkzeug im Job&lt;/li&gt;&#10;&lt;li&gt;X/Y Werkstücknullpunkte anfahren und setzen&lt;/li&gt;&#10;&lt;li&gt;Z-Nullpunkt ermitteln und setzen&lt;/li&gt;&#10;&lt;li&gt;CNC zurück in Warteposition fahren&lt;/li&gt;&#10;&lt;li&gt;Spindelwarmlauf durchführen&lt;/li&gt;&#10;&lt;li&gt;Peripheriegeräte auf Funktion prüfen (Absaugung, Kühlschmierung etc.)&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="während-des-automatikbetriebs"&gt;Während des Automatikbetriebs&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Maschine überwachen&#10;&lt;ol&gt;&#10;&lt;li&gt;Hören: Klingt die Maschine &amp;ldquo;wie immer&amp;rdquo;? Gibt es Vibrationen oder hört sich irgend etwas merkwürdig an?&lt;/li&gt;&#10;&lt;li&gt;Sehen: Ist die Düse für Kühlschmierstoff korrekt ausgerichtet? Alle Parameter am PC im grünen Bereich? Verfahrwege OK?&lt;/li&gt;&#10;&lt;li&gt;Fühlen: Treten Vibrationen der Maschine auf? Führen z.B. Kühlgebläse einen merklichen Luftstrom?&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Falls erforderlich: Werkzeugwechsel durchführen bzw. Werkzeuge vermessen&lt;/li&gt;&#10;&lt;li&gt;Falls etwas nicht in Ordnung scheint: Job per &amp;ldquo;Pause&amp;rdquo;-Taste unterbrechen&lt;/li&gt;&#10;&lt;li&gt;Im Falle eines Falles den Not-Aus betätigen&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="nach-abschluss-des-fräsjobs"&gt;Nach Abschluss des Fräsjobs&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Maschine zurück vor die Referenzschalter bewegen&lt;/li&gt;&#10;&lt;li&gt;Not-Aus betätigen und Maschinenrechner trennen&lt;/li&gt;&#10;&lt;li&gt;Energieversorgung der Maschine abschalten&lt;/li&gt;&#10;&lt;li&gt;Werkstück und gegebenenfalls Reststücke vom Maschinentisch nehmen&lt;/li&gt;&#10;&lt;li&gt;Reinigung von Maschine und Werkstück&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Einschaltstrom ermitteln</title><link>https://blog.schallbert.de/measure-inrush-current/</link><pubDate>Tue, 16 Aug 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/measure-inrush-current/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-16_inrush-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Inrush current measurement"&#10; title="Einschaltstrom ermitteln" /&gt;&#10;&lt;h2 id="vorbereiten-des-multimeters--oszilloskops"&gt;Vorbereiten des Multimeters / Oszilloskops&lt;/h2&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-16_multimeter.jpg" alt="Image: A Multimeter" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Für diesen Artikel habe ich mir ein Multimeter zur Messung von &amp;ldquo;Power Quality&amp;rdquo; direkt vom Hersteller &lt;a href="https://www.gossenmetrawatt.de/en/products/measuring-and-test-technology/multimeters-and-handheld-calibrators/special-applications/multimeters-for-power-and-energy-measurement/metrahit-energy/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;GMC Instruments&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; leihen können. Da dort ein Datenlogger verbaut ist, kann ich die Messungen aufnehmen und später aufbereiten.&lt;/p&gt;&#10;&lt;h3 id="metrahit-energy"&gt;Metrahit Energy&lt;/h3&gt;&#10;&lt;p&gt;Dieses Multimeter ist eher im Profi-Segment angesiedelt. Sowohl der Reichtum an Funktionen als auch sein Preis deuten für mich klar darauf hin. Zusätzlich zum Datenlogger und den üblichen Funktionen eines Multimeters kann es Leistung- und Energie messen bzw. berechnen, harmonische Signalverzerrungen erkennen und nebenbei auch PT100-Temperatursensoren auswerten.&lt;/p&gt;&#10;&lt;h3 id="software-setup"&gt;Software setup&lt;/h3&gt;&#10;&lt;p&gt;So richtet man die Auswertesoftware ein:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Software &lt;a href="https://www.gmc-instruments.de/en/products/metrawin-10" target="_blank" rel="noopener noreferrer" class="external-link"&gt;MetraWin&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; herunterladen und installieren, um die vom Multimeter erfassten Daten zu übertragen und anzuzeigen.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.gmc-instruments.de/en/products/software-and-accessories/software/product-specific-software/test-technology/driver-control/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Driver-Control&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; &amp;ldquo;Treiber&amp;rdquo; herunterladen und installieren. Diese Software dient dazu, die Kommunikation zwischen USB und dem im Lieferumfang des Multimeters befindlichen Infrarot-Schnittstellenadapter bereitzustellen.&lt;/li&gt;&#10;&lt;li&gt;In der MetraWin-Software &lt;code&gt;METRAHit Starline Series&lt;/code&gt; auswählen (übrigens ist mir unklar, warum der Name der Serie weder auf dem Gerät noch auf der Website erscheint)&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-16_metrawin_device.jpg" alt="Image of MetraWin settings to choose device" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/li&gt;&#10;&lt;li&gt;Nun wird der Infrarot-Adapter am PC angeschlossen. Der IR-Adapter sollte im Gerätemanager mit einer &amp;ldquo;COM&amp;rdquo;-Anschluss-ID angezeigt werden (&lt;code&gt;Windows-Taste&lt;/code&gt; &amp;ndash;&amp;gt; &lt;code&gt;Gerät&lt;/code&gt; in die Suche eingeben und &lt;code&gt;Gerätemanager&lt;/code&gt; auswählen &amp;ndash;&amp;gt; nach unten zu &lt;code&gt;Anschlüsse (COM &amp;amp; LPT)&lt;/code&gt; scrollen, aufklappen). Diese Nummer dann in MetraWins &lt;code&gt;Gerät --&amp;gt; Kommunikation... --&amp;gt; SEL-COM&lt;/code&gt; angeben - fertig.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="einrichtung-des-multimeters-für-die-aufzeichnung-des-einschaltstroms"&gt;Einrichtung des Multimeters für die Aufzeichnung des Einschaltstroms&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-16_addr.jpg" alt="Bild: Multimeter-Adresseinstellung" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt; In den Einstellungen dieses Multimeters ist darauf zu achten, dass die Adresse auf einen anderen Wert als &lt;code&gt;00&lt;/code&gt; eingestellt ist (&lt;code&gt;Setup --&amp;gt; Set --&amp;gt; System --&amp;gt; Addr&lt;/code&gt;), da die Infrarot-Kommunikation das Gerät mit einem ungültigen Wert hier nicht erkennt (ich habe dies erlebt und brauchte einige Zeit, um herauszufinden, dass nicht die IR-Adapter-Erkennung fehlerhaft ist, sondern die Einstellungen des Multimeters). Hintergrund ist, dass die Software über die Adressierung bis zu 15 Geräte gleichzeitig unterstützt. Warum &lt;code&gt;00&lt;/code&gt; dabei ungültig ist, das wissen wahrscheinlich nur die Entwickler der Software.&lt;/li&gt;&#10;&lt;li&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-16_rate.jpg" alt="Bild: Multimeter Abtastzeit" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt; Stellen Sie die Abtastzeit auf den niedrigsten Wert für eine gute Impulsverfolgung ein: &lt;code&gt;Measure --&amp;gt; Save --&amp;gt; rate&lt;/code&gt; und wählen &lt;code&gt;0.0005sec&lt;/code&gt; auswählen, was bedeutet, dass der Logger Messungen mit &lt;code&gt;2000Hz&lt;/code&gt; Abtastrate durchführt. Dies ist der niedrigste Wert, den man erreichen kann, und diese Werte beeinträchtigen bereits die Verfügbarkeit des Aufzeichnungskanals, z.B. für Energiemessungen, aufgrund der begrenzten Pufferfähigkeit des Geräts.&lt;/li&gt;&#10;&lt;li&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-16_trms.jpg" alt="Bild: Multimeter TRMS-Einstellung" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt; Wählen Sie nun den Wert aus, den Sie messen wollen (bei mir ist es &lt;code&gt;DC&lt;/code&gt; Strom in &lt;code&gt;Ampère&lt;/code&gt;), und passen über die &lt;code&gt;MAN/AUTO&lt;/code&gt; Taste den Wertebereich im manuellen Betrieb so an, dass die zu erwartende Impulshöhe abgedeckt wird. Kleiner Hinweis: Wenn das Multimeter den Zusatz &lt;code&gt;TRMS&lt;/code&gt; oder &lt;code&gt;RMS&lt;/code&gt; anzeigt, ist dies für unsere Messung hier eher nicht erwünscht, da in diesem Falle eventuelle Spitzenwerte geglättet werden.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Um den Einschaltstrom meines Verstärkers zu bestimmen, verwende ich den Messbereich &lt;code&gt;&amp;gt;10A&lt;/code&gt;. So habe ich zwar eine für den Normalbetrieb geringe Genauigkeit, kann allerdings die Stromspitze gut abbilden.&lt;/p&gt;&#10;&lt;h3 id="alternative-messgeräte"&gt;Alternative Messgeräte&lt;/h3&gt;&#10;&lt;p&gt;Oszilloskope sind Multimetern bei der Zeitauflösung weit überlegen, benötigen aber weitere Hilfsmittel um Ströme exakt erfassen zu können.&lt;/p&gt;&#10;&lt;p&gt;Dennoch sind sie hier mit einfachen Hilfsmitteln verwendbar: Einen Widerstand mit niedrigem Wert (z.B. &lt;code&gt;0.1 Ohm&lt;/code&gt;) und geringer Toleranz in Reihe mit dem Prüfling schalten und den Spannungsabfall am Widerstand per Oszilloskop bestimmen. Bei einer Auflösung von &lt;code&gt;1V/div&lt;/code&gt; läge dann die Umrechnung für den Einschaltstrom bei &lt;code&gt;10A/division&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Ein günstiges Multimeter hingegen mit &amp;ldquo;Data Max Hold&amp;rdquo; Funktion wird für diesen Anwendungsfall wahrscheinlich aber nicht funktionieren, da die Messintervalle für die zu messenden, sich schnell aufladenden Kondensatoren schlicht zu lang sind.&lt;/p&gt;&#10;&lt;h2 id="der-prüfling"&gt;Der Prüfling&lt;/h2&gt;&#10;&lt;p&gt;Die zu untersuchende Schaltung ist eine Endstufe mit &lt;code&gt;30W&lt;/code&gt; Dauerleistung &lt;a href="https://store.sure-electronics.com/product/AA-AB32996" target="_blank" rel="noopener noreferrer" class="external-link"&gt;AA-AB32996&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, produziert von &amp;ldquo;sure electronics&amp;rdquo;. Er basiert auf dem integrierten Class-D-Verstärkerbaustein &lt;a href="https://www.ti.com/product/TPA3130D2" target="_blank" rel="noopener noreferrer" class="external-link"&gt;TPA3110&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-16_TPA3110.jpg" alt="Image: sure electronics AA-AB32996 based on TPA3110" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die Energieversorgung besitzt einen Verpolschutz (drei parallel geschaltete Schottkydioden) und bindet sechs Pufferkondensatoren mit einer Kapazität von je &lt;code&gt;220uF&lt;/code&gt; ein. Zusätzlich befinden sich nahe der Versorgungspins des IC zwei weitere Entstörkondensatoren.&lt;/p&gt;&#10;&lt;p&gt;Zum Vergleich: Das Referenzlayout des IC-Herstellers sieht der oben beschriebenen Lösung recht ähnlich. Hier werden zusätzlich &lt;code&gt;FB&lt;/code&gt; (Ferritkerne) verwendet, um die Ströme auf der Eingangsseite zu glätten. Dafür wird weniger Pufferkapatizät vorgesehen.&#10;Darüber hinaus verwendet das Referenzdesign einfache Ferritkerne anstatt hochwertigerer Filterspulen am Verstärkerausgang.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-16_TPA3110_recommended_layout.jpg" alt="Image: TPA3110 recommended PCB layout" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="motivation-ausfall-eines-prototypen-bei-verwendung-von-usb-c-pd"&gt;Motivation: Ausfall eines Prototypen bei Verwendung von USB-C PD&lt;/h3&gt;&#10;&lt;p&gt;Für einen meiner Prototypen hatte ich vorher ein USB-C Power Delivery Entwicklungsboard - &lt;a href="https://blog.schallbert.de/usbpd-explained/#stusb4500"&gt;erklärt in diesem Beitrag&lt;/a&gt; - verwendet. Leider musste ich feststellen, dass dieser nach nur ein paar Einschaltzyklen in Rauch aufging - wahrscheinlich verursacht durch einen hohen Einschaltstrom, für den der IC nicht ausgelegt ist.&lt;/p&gt;&#10;&lt;p&gt;Daher wollte ich genau wissen, wie hoch die Stromspitzen sind und mit welcher Komponente ich den Einschaltstrom so in den Griff bekomme, dass hierdurch verursachte Schäden an elektronischen Bauteilen ausgeschlossen werden können.&lt;/p&gt;&#10;&lt;h2 id="durchführung-der-messung"&gt;Durchführung der Messung&lt;/h2&gt;&#10;&lt;p&gt;Ich verbinde den Messpfad für Strom des Multimeters mit dem Minuspol des Verstärkers und die Masse des Multimeters mit dem Minuspol des Netzteils. Die Messspitze für Spannung verbinde ich mit dem Pluspol des Verstärkers, und letzteren wiederum schließe ich über einen Schalter am Netzteil an.&lt;/p&gt;&#10;&lt;p&gt;Um die Messung zu beginnen, wähle ich beim Multimeter &lt;code&gt;SET --&amp;gt; Store --&amp;gt; Start --&amp;gt; Store&lt;/code&gt; und lege den Schalter an der Spannungsversorgung um. Nach ein paar Sekunden beende ich die Messung mit der Taste &lt;code&gt;Stop&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Da ich zu beginn die höchstmögliche Datenrate eingestellt hatte, musste ich die Daten hernach manuell an den Computer übermitteln und konnte nicht die Liveübertragung wählen. Hierfür wählt man in der Software folgende Einstellung aus: &lt;code&gt;Read Memory --&amp;gt; Get Memory Info --&amp;gt; Move Memory Data to File --&amp;gt; save --&amp;gt; Open Memory Data File --&amp;gt; Display Data from File&lt;/code&gt;. Mir ist dabei nicht ganz klar, warum an der Stelle so viele Schritte erforderlich sind - ich hätte hierfür nur einen Knopfdruck erwartet.&lt;/p&gt;&#10;&lt;p&gt;Das Dateiformat &lt;code&gt;Measurement Data File&lt;/code&gt; hat einen großen Vorteil gegenüber dem ebenfalls möglichen &lt;code&gt;Memory Data File&lt;/code&gt;: Es enthält Metadaten wie Messdatenauswahl für die Ansicht, Labels und Details zur Darstellung wie Einheiten etc. Ohne großen Aufwand lassen sich Auswertungen erstellen und beispielsweise im PDF-Dateiformat exportieren.&lt;/p&gt;&#10;&lt;h3 id="visualisierung-des-einschaltstroms"&gt;Visualisierung des Einschaltstroms&lt;/h3&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-16_TPA3110_inrushcurrent.jpg" alt="Image: TPA3110 current over time" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Der ganze Spuk dauert nur &lt;code&gt;~10ms&lt;/code&gt;, also 20 samples. Nach einem hohen Strompuls von &lt;code&gt;&amp;gt;12A&lt;/code&gt; zeigt sich ein Plateau bei rund &lt;code&gt;4A&lt;/code&gt; bis sich die Kondensatoren aufgeladen haben. Danach geht der Strom zurück, bis er dem Leerlaufstrom des Verstärkers entspricht.&lt;/p&gt;&#10;&lt;p&gt;Der Spitzenwert liegt allerdings nur für einen Datenpunkt vor - daher kann ich nicht sagen, ob es sich um einen Messfehler / Ausreißer oder ein tatsächliches Ereignis handelt. Ein paar Annahmen zur Ursache:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Es wurden schaltungsweit Entkopplungskondensatoren mit sehr geringem ESR (Serienwiderstand) verwendet&lt;/li&gt;&#10;&lt;li&gt;Verlassen des Messbereichs des Multimeters&lt;/li&gt;&#10;&lt;li&gt;Limiter des Labornetzteils begrenzt den Strom auf Niveau des Plateaus (war auf &lt;code&gt;5A&lt;/code&gt; konfiguriert, passt also nicht recht zusammen)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="spannungsverlauf-an-den-versorgungspins-des-verstärkers"&gt;Spannungsverlauf an den Versorgungspins des Verstärkers&lt;/h3&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-08-16_TPA3110_inrushvoltage.jpg" alt="Image: TPA3110 voltage over time" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die Spannungsmessung zeigt einen sprunghaften Anstieg beim ersten Messwert nach Einschalten des Verstärkers, gefolgt von einem fast linearen Anstieg bis die Quellspannung nach etwa &lt;code&gt;~6ms&lt;/code&gt; erreicht wird.&lt;/p&gt;&#10;&lt;p&gt;Diese Werte passen zeitlich prima mit den Strommessungen zusammen und bestätigen sowohl Impuls- als auch Plateauwerte.&lt;/p&gt;&#10;&lt;h3 id="wiederholung--ergebnis"&gt;Wiederholung &amp;amp; Ergebnis&lt;/h3&gt;&#10;&lt;p&gt;Ich wiederholte die Messung mit unterschiedlichen Randbedingungen: Lautsprecher angeschlossen/ nicht verbunden, Verstärker in Standby/ Leerlauf usw. aber die Messergebnisse waren stets ähnlich. Somit wird die Verstärkerschaltung im Moment des Einschalten nicht durch seine Konfiguration oder Peripherie beeinflusst.&lt;/p&gt;&#10;&lt;p&gt;Die Werte sind also bedingt durch den Versorgungspfad und seine Schutzmaßnahmen, Energiespeicher und seine Entkopplungsschaltung.&lt;/p&gt;&#10;&lt;p&gt;Nachteile der Messung mittels Multimeter sind geringe Datenrate (10-100kHz wären besser gewesen), keine Möglichkeit, die Messwerte live abzulesen, und langen Datentransferzeiten zum PC, geschuldet durch die Kommunikation per Infrarot. Andererseits kann ich das Multimeter jetzt schnell mal als &amp;ldquo;offline Datenlogger&amp;rdquo; auch über längere Zeit verwenden, ohne einen Computer zur Konfiguration dabei haben zu müssen. Optimal für die Anwendung im Feld.&lt;/p&gt;&#10;&lt;p&gt;Für den Verstärker werde ich mit diesen Erkenntnissen jetzt eine Schutzschaltung entwickeln, ähnlich wie in &lt;a href="https://blog.schallbert.de/inrushlimiter-circuits/"&gt;diesem Beitrag diskutiert&lt;/a&gt;, und so in Zukunft Probleme mit USB-PD ICs vermeiden.&lt;/p&gt;&#10;</description></item><item><title>Meine CNC vibriert</title><link>https://blog.schallbert.de/cnc-vibrates/</link><pubDate>Thu, 14 Jul 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cnc-vibrates/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-07-14_vibrations-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Distorted image of a circle cut with my CNC"&#10; title="Meine CNC vibriert" /&gt;&#10;&lt;h2 id="hintergrund"&gt;Hintergrund&lt;/h2&gt;&#10;&lt;p&gt;Langsam werde ich mutiger im Umgang mit meiner Maschine. In letzter Zeit bearbeite ich des Öfteren Birke Multiplex in der Vollnut, möglichst mit nur einem Fräsdurchgang um nicht so lange im kalten Keller stehen zu müssen. Das bedeutet aber auch, dass die Maschine eine Tiefenzustellung &lt;code&gt;Z+&lt;/code&gt; von &lt;code&gt;12mm&lt;/code&gt; bei &lt;code&gt;F2700mm/min&lt;/code&gt; mit einem 6mm 2-Schneider Schruppfräser bei &lt;code&gt;S=24000rpm``Z+&lt;/code&gt; bewältigen muss. Dabei fräse ich im Gleichlauf.&lt;/p&gt;&#10;&lt;p&gt;Nun fängt meine Maschine in bestimmten Situation an, hörbar zu vibrieren.&lt;/p&gt;&#10;&lt;p&gt;Nun liegt es nahe, die Schneidkräfte an der Maschine zu reduzieren, entweder durch Reduktion der Tiefenzustellung oder des Vorschubs. Die Drehzahl kann ich bei meinem Fräsmotor nämlich nicht weiter erhöhen. Das werde ich vielleicht auch tun, aber erst einmal möchte ich verstehen, was die Fräse zum Vibrieren bringt und ob ich das auch durch andere Maßnahmen abstellen kann, ohne die Maschinenzeit zu erhöhen oder Standzeiten meiner Fräser merklich zu reduzieren.&lt;/p&gt;&#10;&lt;h2 id="problembeschreibung"&gt;Problembeschreibung&lt;/h2&gt;&#10;&lt;p&gt;Die Maschine vibriert nicht nur bei Konturschnitten, sondern auch wenn ich Taschen fertige. Allerdings nur bei bestimmten Bewegungsrichtungen. Es fühlt sich so an, als würde sich die ganze Maschine leicht schütteln, und das Geräusch ähnelt einem Stuhl, der unter Last über Parkettboden zurückgeschoben wird.&lt;/p&gt;&#10;&lt;p&gt;Um das etwas zu verdeutlichen, habe ich zwei kleine Referenzvideos gemacht.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-07-14-machine_vibrations_pocketing.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Vibrationen nur an bestimmten Stellen beim Taschenfräsen (80% Räumzustellung)&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-07-14-machine_vibrations_slot.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Vibrationen beim Formatieren&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="analyse"&gt;Analyse&lt;/h2&gt;&#10;&lt;p&gt;Das Fräsbild sieht trotz Vibrationen ordentlich aus.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-07-14_cut_results.jpg" alt="Image: Despite vibrating, the cut still looks clean" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;p&gt;Ich habe versuchsweise ein paar Einstellungen angepasst: Reduzierte Tiefenzustellung &lt;code&gt;Z+&lt;/code&gt; von nur noch &lt;code&gt;6mm&lt;/code&gt; bei erhöhtem Vorschub von &lt;code&gt;3600mm/min&lt;/code&gt; mit demselben Fräser - Vibrationen abgestellt.&lt;/p&gt;&#10;&lt;p&gt;Nun war es zugegebenermaßen nicht so klug, zwei Parameter gleichzeitig zu ändern - so weiß ich nicht, ob die erhöhten Schnittkräfte oder ein Vorschub im Resonanzbereich der Maschine zu den Vibrationen lag - tsja.&lt;/p&gt;&#10;&lt;p&gt;Dennoch habe ich den Maschinenhersteller auf das Problem angesprochen &lt;a href="https://hobbyline.info/forum/index.php?thread/686-meine-maschine-vibriert-erledigt/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;hier verlinkt&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, und seiner Meinung nach ist der wahrscheinlichste Grund die etwas schwach ausgelegte Verbindung zwischen Fräsmotor und Z-Achse.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-07-14_routermount.jpg" alt="Image: Router mounting to the Z-axis" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Der Fräsmotor hat weniger hochwertige Lager als professionelle Hochfrequenzspindeln, also sind sie potentiell eher von Spiel betroffen. Außerdem klemmt die &lt;code&gt;42mm&lt;/code&gt; Eurohals-Halterung den Fräsmotor mit nur einer Verschraubung, die zudem bei recht niedrigen &lt;code&gt;7Nm&lt;/code&gt; drehmomentbegrenzt ist, da der vordere Lagersitz des Fräsmotors sonst zu stark gequetscht wird.&lt;/p&gt;&#10;&lt;p&gt;Im Gegensatz dazu werden Motorspindeln meist direkt und vollflächig mit der Z-Achse verschraubt, sodass Kraftschluss und Kontaktfläche viel größer ausfallen.&lt;/p&gt;&#10;&lt;h2 id="ergebnis"&gt;Ergebnis&lt;/h2&gt;&#10;&lt;p&gt;Ich habe jetzt ein paar Optionen:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Weiter mit großer Tiefenzustellung arbeiten und die Vibrationen und damit einhergehenden erhöhten Maschinenverschleiß tolerieren&lt;/li&gt;&#10;&lt;li&gt;Geringere Zustellungen fahren, dafür aber Maschinenzeit opfern&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/projects/spindle-upgrade/"&gt;Die Spindel aufrüsten&lt;/a&gt;, welches meine Maschine deutlich teurer macht&lt;/li&gt;&#10;&lt;li&gt;Mein &lt;a href="https://blog.schallbert.de/why-vacuum-table/"&gt;Maschinenbett aufrüsten&lt;/a&gt; und so die Steifigkeit des Grundrahmens erhöhen&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="update-september-2023"&gt;Update September 2023&lt;/h2&gt;&#10;&lt;p&gt;So, ich habe jetzt sowohl die Spindel als auch das Maschinenbett aufgerüstet. Die Vibrationen sind leider immer noch da. Das finde ich ziemlich ärgerlich.&lt;/p&gt;&#10;&lt;p&gt;Wenn ich mir die Umstände nochmal genau ansehe, dann treten die Vibrationen immer dann auf, wenn sich das Portal vorwiegend auf der breiten Achse (bei mir X) bewegt, gleichzeitig aber ebenfalls leicht auf der Längsachse (Y) unterwegs ist. Fahre ich nur in &lt;code&gt;Y&lt;/code&gt;, passiert nichts. Fahre ich nur in &lt;code&gt;X&lt;/code&gt;, treten die Vibrationen schwächer auf.&lt;/p&gt;&#10;&lt;p&gt;Ich kan inzwischen ebenfalls sagen, dass die Vibrationen in schwächerer Form auch in Acrylglas bei einer Tiefenzustellung von &lt;code&gt;Z+5mm&lt;/code&gt;, Vorschub &lt;code&gt;F3000mm/min&lt;/code&gt; und einem 4mm-Einschneider auftreten.&lt;/p&gt;&#10;&lt;p&gt;Ich werde einmal ausprobieren, ob ich das Problem auch noch bei &lt;code&gt;F3600mm/min&lt;/code&gt; habe.&lt;/p&gt;&#10;&lt;p&gt;Da ich inzwischen Maschinenbett und Spindelbefestigung als Ursache sicher ausschließen kann, liegt die Ursache der Vibrationen meiner Einschätzung nach am Zusammenspiel der Portalachsen.&lt;/p&gt;&#10;&lt;p&gt;Nun habe ich eine recht breite Maschine ohne zusätzlich verstärkte Portalwangen, die zudem nur mit einer statt zwei Linearführungen auf der Z-Achse ausgestattet ist. Ich spreche die nächsten Wochen nochmal mit dem Hersteller, um diesen Verdacht zu erhärten bzw. aus dem Weg zu räumen.&lt;/p&gt;&#10;</description></item><item><title>Vom Bild zum Pfad</title><link>https://blog.schallbert.de/path-from-image/</link><pubDate>Mon, 04 Jul 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/path-from-image/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-07-04_cam-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: CAM drawing of Image-to-Path engraving"&#10; title="Vom Bild zum Pfad" /&gt;&#10;&lt;p&gt;Dies ist ein kurzer Beitrag über die Verwendung der beiden Open-Source-Programme &lt;a href="https://www.getpaint.net/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Paint.NET&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und &lt;a href="https://inkscape.org/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Inkscape&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, um aus Pixelgrafiken Pfade zu erzeugen. Diese können dann z.B. für Gravuren auf einer CNC-Maschine, einem Laserschneider oder einem Plotter verwendet werden. Und das sogar auf Grundlage eines digitalen Bildes von geringer Qualität in einem verlustbehafteten Datenkompressionsformat wie JPEG.&lt;/p&gt;&#10;&lt;p&gt;Der Beitrag wurde inspiriert von &lt;a href="https://graphicdesign.stackexchange.com/questions/113560/convert-parallel-paths-into-one-in-inkscape" target="_blank" rel="noopener noreferrer" class="external-link"&gt;diesem ausgezeichneten Stackexchange-Thread&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 id="die-bildquelle"&gt;Die Bildquelle&lt;/h2&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-07-04_original.jpg" alt="Image: Lagerfeuer - wunderschön gezeichnet von Schallbert" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Alles beginnt mit zu konvertierenden dem Bild (bzw. der Pixelgrafik).&lt;/p&gt;&#10;&lt;h3 id="geistiges-eigentum"&gt;Geistiges Eigentum&lt;/h3&gt;&#10;&lt;p&gt;Zu beachten ist, dass Bilder (auch selbstgezeichnete) in der Regel urheberrechtlich geschützt sind, so dass zunächst die (schriftliche) Zustimmung des Eigentümers des geistigen Eigentums eingeholt werden muss.&lt;/p&gt;&#10;&lt;p&gt;Sind Sie der Inhaber der geistigen Eigentumsrechte, ist das viel einfacher. Aus diesem Grund habe ich für diese kurze Demo ein Bild gewählt, das ich selbst erstellt habe.&lt;/p&gt;&#10;&lt;h3 id="bildqualität"&gt;Bildqualität&lt;/h3&gt;&#10;&lt;p&gt;Richtig gut zeichnen kann ich allerdings nicht, wie das Bild klar andeutet. Einige Linien sind übereinander gemalt und der Kontrast ist gering. Perfekte Bedingungen, um zu zeigen, was die Software leisten kann!&lt;/p&gt;&#10;&lt;p&gt;Hinweis: Es gilt wie immer das Credo &amp;ldquo;Garbage in, garbage out&amp;rdquo;. Es ist also sicher zu stellen, dass vor Beginn der Arbeiten die bestmögliche Bildqualität erreicht wird. Auf diese Weise ist die Bereinigung des Bildes viel einfacher und die Gravurpfade fallen simpler und genauer aus.&lt;/p&gt;&#10;&lt;h2 id="paintnet"&gt;Paint.NET&lt;/h2&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-07-04_luminance.jpg" alt="Image: Luminance change for maximum contrast" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="farbkorrektur-und-freistellung"&gt;Farbkorrektur und Freistellung&lt;/h3&gt;&#10;&lt;p&gt;Schneiden Sie Ihr Bild zunächst auf den Inhalt zu, den Sie später verwenden möchten. Nehmen Sie dann das Werkzeug &lt;code&gt;Helligkeitskurve&lt;/code&gt; (Anpassen -&amp;gt; Kurven) und versuchen Sie, die hellen und dunklen Töne so weit wie möglich zu trennen.&lt;/p&gt;&#10;&lt;p&gt;Zweitens, wenn Ihr Bild so schlecht ist wie meines, verwenden Sie das Werkzeug &lt;code&gt;Zauberstab&lt;/code&gt;. Spielen Sie mit den Toleranzen für die Farberkennung - um so den Hintergrund so gut wie möglich zu entfernen und das Motiv freizustellen.&lt;/p&gt;&#10;&lt;p&gt;Drittens: Verwenden Sie das &lt;code&gt;Kontur&lt;/code&gt;-Werkzeug (Effekte -&amp;gt; Stilisieren -&amp;gt; Kontur), um eine schöne dicke Linie als Umriss zu erhalten. Eine Umwandlung des Bildes in Graustufen und eine weitere Erhöhung des Kontrasts können helfen, diese Linie schwarz zu machen. Falls Sie Linien verlieren, die nicht dunkel genug sind - keine Sorge, das lässt sich später in Inkscape leicht beheben.&lt;/p&gt;&#10;&lt;h3 id="weitere-bereinigung"&gt;Weitere Bereinigung&lt;/h3&gt;&#10;&lt;p&gt;Entfernen Sie so viele &amp;ldquo;Pixelwolken&amp;rdquo; und andere Artefakte aus dem Bild wie möglich mit den Werkzeugen &lt;code&gt;Zauberstab&lt;/code&gt; oder &lt;code&gt;Radiergummi&lt;/code&gt;, das ist jetzt einfacher als später.&lt;/p&gt;&#10;&lt;p&gt;Speichern Sie Ihr Bild als portable Netzwerkgrafik &lt;code&gt;.PNG&lt;/code&gt;.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-07-04_outline.jpg" alt="Image: Outline tool for continuous and thick lines" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="inkscape"&gt;Inkscape&lt;/h2&gt;&#10;&lt;p&gt;Öffnen Sie das Bild in Inkscape. Wählen Sie die Standard-Import-Einstellungen &lt;code&gt;DPI der Datei verwenden&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="nachzeichnen"&gt;Nachzeichnen&lt;/h3&gt;&#10;&lt;p&gt;Wählen Sie &lt;code&gt;Pfad -&amp;gt; Bitmap nachzeichnen&lt;/code&gt;. In dem neu geöffneten Tab wählen Sie &lt;code&gt;Trace Bitmap&lt;/code&gt;, &lt;code&gt;Single Scan&lt;/code&gt;, &lt;code&gt;Brightness Cutoff&lt;/code&gt; und spielen mit den Optionen herum, bis sich die Kontur Ihres Motivs vom Hintergrund abhebt. Stellen Sie sicher, dass Sie das Bild per Mausklick auswählen, bevor Sie auf &amp;ldquo;Aktualisieren&amp;rdquo; oder &amp;ldquo;Anwenden&amp;rdquo; klicken. Wenn Sie mit der Vorschau nicht zufrieden sind, passen Sie die Werte an. Falls Sie bei der Vielzahl der Einstellungen nicht weiterkommen, schauen Sie sich einfach &lt;a href="https://inkscape.org/doc/tutorials/tracing/tutorial-tracing.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;dieses Tutorial von Inkscape - Link zur Inkscape Website&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; an.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-07-04_paths.jpg" alt="IMAGE Image to Path tool at its best" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;OK, jetzt haben wir eine Menge Pfade. Per Klick auf &lt;code&gt;Pfad -&amp;gt; Vereinfachen&lt;/code&gt; wird die Anzahl der Stützpunkte reduziert. Entfernen Sie dann manuell Artefakte, die sich bei Verwendung der Luminanzkurven eingeschlichen haben, mit dem &lt;code&gt;Knotenwerkzeug&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="füllen-und-streichen"&gt;Füllen und Streichen&lt;/h3&gt;&#10;&lt;p&gt;Damit gibt es nun genau zwei die Zeichnung außen und innen umreißende Pfade sowie eine Füllung zwischen diesen Pfaden, die die Linienstärke der ursprünglichen Zeichnung darstellt. Da wir nur die äußere Kontur behalten wollen, müssen wir die Füllung und die innere Linie entfernen.&lt;/p&gt;&#10;&lt;p&gt;Wählen Sie das Werkzeug &lt;code&gt;Linie und Füllung&lt;/code&gt; im unteren Teil der rechten Symbolleiste. Wählen Sie die Registerkarte &lt;code&gt;Linienfarbe&lt;/code&gt; und setzen Sie den Strich auf eine &lt;code&gt;deckende Farbe&lt;/code&gt;. Wählen Sie unter &lt;code&gt;Linienstil&lt;/code&gt; eine Linienbreite, die Ihnen zusagt. Gehen Sie dann zur Registerkarte &lt;code&gt;Ausfüllen&lt;/code&gt; und stellen Sie &lt;code&gt;Ohne&lt;/code&gt; ein. Jetzt haben Sie nur noch zwei Umrisslinien, von denen Sie eine löschen können, wenn Sie möchten.&lt;/p&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-07-04_paths_cleaned.jpg" alt="Image: Paths cleaned up and simplified" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="und-noch-mehr-bereinigung"&gt;Und noch mehr Bereinigung&lt;/h3&gt;&#10;&lt;p&gt;Das sieht doch schon ziemlich gut aus, oder? Wenn es Ihnen nichts ausmacht, dass die Umrisse mehr als ein einziger Strich sind, sind Sie schon fertig. Sie können nun das Bild weiter verbessern, indem Sie Pfade herausziehen oder Kurven umwandeln.&lt;/p&gt;&#10;&lt;p&gt;Verwenden Sie das Werkzeug &lt;code&gt;Pfad an ausgewählten Knoten unterbrechen&lt;/code&gt;, bevor Sie parallele Linien entfernen, damit Sie nicht versehentlich die Linien zerstören, die Sie behalten möchten. Wenn Sie mit dem Ergebnis zufrieden und alle Unsauberkeiten ausgebügelt sind, speichern Sie Ihr Werk als skalierbare Vektorgrafikdatei &lt;code&gt;SVG&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Das war&amp;rsquo;s! Jetzt kann Ihr CAM-System das Bild importieren (denken Sie an die DPI-Einstellung, damit Ihre Skalierung gültig bleibt).&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-07-04_clean.jpg" alt="Image: Fireplace SVG path work complete" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>CNC: Überlastung Fräsmotor</title><link>https://blog.schallbert.de/cnc-router-overload/</link><pubDate>Sat, 18 Jun 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cnc-router-overload/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-18_overload-thumb.jpg"&#10; class="post-cover"&#10; alt="Image of an endmill path that doesn&amp;#39;t look good at all"&#10; title="CNC: Überlastung Fräsmotor" /&gt;&#10;&lt;h2 id="was-ich-eigentlich-wollte"&gt;Was ich eigentlich wollte&lt;/h2&gt;&#10;&lt;p&gt;Ich hatte eine Anfrage zum Schneiden und Gravieren von Hochdrucklaminaten. Dabei handelt es sich um eine Lasagne aus gepresstem Papier und &lt;a href="https://en.wikipedia.org/wiki/Phenol_formaldehyde_resin" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Phenolharz&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, überzogen mit einer dünnen Schicht (farbigen) &lt;a href="https://en.wikipedia.org/wiki/Melamine_resin" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Melaminharzes&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. HPL ist so widerstandsfähig, dass es für Fassadenbeplankungen verwendet wird. Es ist spülmaschinenfest und einige Varianten können auf bis zu 200°C erhitzt werden, ohne Schaden zu nehmen. HPL findet man als Platten auf Spielplätzen, Gartentischen, als Paneele in der Straßenbahn und an vielen anderen Stellen im Alltag.&#10;Also dachte ich mir: &amp;ldquo;Cooles Zeug, Probieren geht über Studieren!&amp;rdquo;&lt;/p&gt;&#10;&lt;h2 id="schnittdaten-in-hochdrucklaminaten-hpl-resopal"&gt;Schnittdaten in Hochdrucklaminaten (HPL, &amp;ldquo;Resopal&amp;rdquo;)&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-18_overload_chips.jpg" alt="Image: HPL und Späne nach dem Schneiden" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich habe die von einigen Websites empfohlenen Schnittdaten mit Werten für &amp;ldquo;Hartholz&amp;rdquo; verwendet.&#10;2-Schneider.Holz-Hartmetallschaftfräser, Schnittgeschwindigkeit &lt;code&gt;450m/min&lt;/code&gt;, &lt;code&gt;d6mm&lt;/code&gt;, &lt;code&gt;Z+6,5mm&lt;/code&gt;, &lt;code&gt;S24000rpm&lt;/code&gt;, &lt;code&gt;F3500mm/min&lt;/code&gt;, Vollnut, Gleichlauffräsen.&lt;/p&gt;&#10;&lt;h2 id="schnittergebnisse"&gt;Schnittergebnisse&lt;/h2&gt;&#10;&lt;p&gt;Und ich war erschrocken, wie der Schnitt aussah, als ich fertig war.&#10;Hier sind die Symptome:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Während des Schneidens fiel die Spindeldrehzahl wiederholt ab, stieg aber schnell wieder auf die gewünschte Drehzahl an.&lt;/li&gt;&#10;&lt;li&gt;hohe Spanlast am Fräser, große Späne, sah alles gut aus&lt;/li&gt;&#10;&lt;li&gt;der Fräser folgte keiner geraden Linie: schien dem Material irgendwie ausweichen zu wollen.&lt;/li&gt;&#10;&lt;li&gt;bei der Herstellung von Anbindungen, um das Werkstück an Ort und Stelle zu halten, bewegt sich der Fräser ein wenig quer zur Fräsbahn, obwohl er das nicht sollte&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="suche-nach-dem-übeltäter"&gt;Suche nach dem Übeltäter&lt;/h2&gt;&#10;&lt;p&gt;Zuerst habe ich die Spindel überprüft. Keine Überhitzung, immer noch fest an der Portalfräse eingespannt, kein Lagerspiel, Schaftfräser sicher befestigt. Dann dachte ich: &amp;ldquo;Vielleicht stimmt etwas mit dem Portal nicht? Kann ich es merklich verwinden?&amp;rdquo; Nein, das konnte ich nicht. Dann sah ich mir den Schaftfräser an.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-18_overload_endmill.jpg" alt="Image: dull carbide endmill after cutting HPL" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Es sah überhaupt nicht gut aus. Die Schnittkanten waren an den Stellen, an denen sie das Material berührt hatten, praktisch weg. Seltsam. Es war nagelneu als ich den Auftrag begann, und der lief nicht einmal 20 Minuten lang.&lt;/p&gt;&#10;&lt;h2 id="analyseergebnisse"&gt;Analyseergebnisse&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Ich habe viel zu aggressive Schnittdaten verwendet.&lt;/li&gt;&#10;&lt;li&gt;Dadurch wurde der Motor der Oberfräse weit über seine Möglichkeiten hinaus betrieben.&lt;/li&gt;&#10;&lt;li&gt;Dies führte zu einem plötzlichen Abfall der Motordrehzahl.&lt;/li&gt;&#10;&lt;li&gt;Dies wiederum führte zu einer erhöhten Spanbelastung meines Fräsers&lt;/li&gt;&#10;&lt;li&gt;Da die Lager und die Befestigung des Fräsmotors am Portal &amp;ldquo;weich&amp;rdquo; sind, versuchte der Fräser, seiner Fräsbahn auszuweichen&lt;/li&gt;&#10;&lt;li&gt;Dies hat mich wahrscheinlich vor dem Bruch des Fräsers bewahrt.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="verbesserungen"&gt;Verbesserungen&lt;/h2&gt;&#10;&lt;h3 id="neubewertung-der-schnittparameter-für-hochdrucklaminat"&gt;Neubewertung der Schnittparameter für Hochdrucklaminat&lt;/h3&gt;&#10;&lt;p&gt;Ich habe nochmal online recherchiert. HPL ist unglaublich hart und steif. Hier mal ein paar Werte zum Vergleich (Brinellhärte):&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Buche: &lt;code&gt;~3.7N/mm²&lt;/code&gt; (&lt;a href="https://www.olivenholzprodukte.de/Holzhaerte-Tabelle-nach-Brinell" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Source&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;)&lt;/li&gt;&#10;&lt;li&gt;HPL: &lt;code&gt;~185N/mm²&lt;/code&gt; (&lt;a href="https://www.pro-hpl.org/assets/uploads/prohpl/files/200330_Technische_Kennziffern_und_physikalische_Eigenschaften_von_HPL.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Source&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;)&lt;/li&gt;&#10;&lt;li&gt;Baustahl ST70: &lt;code&gt;215N/mm²&lt;/code&gt; (&lt;a href="https://www.ingenieurkurse.de/baustofftechnik-1/stoffeigenschaften-im-bauwesen/mechanische-eigenschaften-von-baustoffen/oberflaechenhaerte/brinellhaerte.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Source&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;)&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Des Weiteren fand ich einige Empfehlungen zur spanabhebenden Verarbeitung von HPL, z.B.: &amp;ldquo;Vollhartmetallfräser sollten eine Schnittgeschwindigkeit von bis zu &lt;a href="https://www.dekorplatten.de/sites/default/files/dateien/HPL-Verarbeitungshinweise.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;code&gt;150m/min&lt;/code&gt;&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&amp;rdquo; und &amp;ldquo;Diamantbeschichtungen aufweisen oder als Einschneider zur Bearbeitung von Aluminium ausgeführt sein&amp;rdquo;.&lt;/p&gt;&#10;&lt;h3 id="zwischenlösung"&gt;Zwischenlösung&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-18_overload_corrected.jpg" alt="Image: dull carbide endmill after cutting HPL" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Um die Dinge zu korrigieren, habe ich alle Ausschnitte mit den Schlicht-Einstellungen in CAM erneut durchgeführt. Mit einem neuen Schaftfräser und weniger aggressiven Vorschubgeschwindigkeiten von &lt;code&gt;F2500mm/min&lt;/code&gt; und einer aktualisierten Tiefe pro Durchgang von &lt;code&gt;Z+1,5mm&lt;/code&gt;.&#10;Das Ergebnis sieht jetzt viel besser aus.&lt;/p&gt;&#10;&lt;h3 id="optimierung-der-schnittdaten"&gt;Optimierung der Schnittdaten&lt;/h3&gt;&#10;&lt;p&gt;Dann berechnen wir schnell mal neue Schnittdaten aufgrund der oben gewonnenen Erkenntnisse. Ich wähle einen &lt;code&gt;4mm&lt;/code&gt; Einschneider (&lt;code&gt;z=1&lt;/code&gt;) mit Beschichtung zur besseren Wärmeabfuhr für Aluminium und lege die oben genannte Schnittgeschwindigkeit von &lt;code&gt;vc=150m/min&lt;/code&gt; für HPL zugrunde. Weiterhin wähle ich einen Zahnvorschub von &lt;code&gt;fTooth = 0,025mm&lt;/code&gt;, ähnlich wie beim Fräsen von Stahl.&lt;/p&gt;&#10;&lt;p&gt;Zieldrehzahl: &lt;/p&gt;&#10;$$n=\frac{v_c}{\pi d}=\frac{150000\frac{mm}{min}}{\pi \cdot 4mm}=12000\frac{1}{min}$$&lt;p&gt;Zielvorschub: &lt;/p&gt;&#10;$$F=n \cdot f_{Tooth} \cdot z = 12000\frac{1}{min} \cdot 0,025mm \cdot min \cdot 1= 300\frac{mm}{min}$$&lt;p&gt;Ich habe einen &lt;code&gt;4mm&lt;/code&gt;-Schaftfräser gewählt, weil das Drehmoment meines Fräsmotors bei sinkender Drehzahl überproportional abnimmt. Bei größerem Fräserdurchmesser würde dieser Effekt wahrscheinlich zu einer unzureichenden Schnittleistung führen. Spindeln mit einer Asynchronmaschine sind hier durch ihr konstant hohes Drehmoment im unteren Drehzahlbereich (bei Vektorsteuerung kann es sogar deutlich über dem Nennmoment des Motors liegen) ganz klar im Vorteil.&lt;/p&gt;&#10;&lt;p&gt;Der berechnete Vorschub kommt mir so langsam vor. Ich hoffe, dass sich das Material dadurch nicht so stark erhitzt, dass es delaminiert (laut Wikipedia schmilzt es nicht&amp;hellip;)&lt;/p&gt;&#10;&lt;p&gt;Zur Information: &lt;a href="https://hobbyline.info/forum/index.php?thread/678-hpl-platten-fr%C3%A4sen/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Hier ist der zugehörige Thread&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; in dem Forum, das ich regelmäßig besuche.&lt;/p&gt;&#10;&lt;h3 id="weitere-tests"&gt;Weitere Tests&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-18_coated_new.jpg" alt="Image: new coated carbide endmill for aluminium" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich habe einige Teile mit oben angesprochenem Fräser und Einstellungen gefertigt. Um die Fertigungszeiten etwas im Zaum zu halten, habe ich die Werte leicht angepasst: &lt;code&gt;ftooth = 0.04mm&lt;/code&gt; und &lt;code&gt;vc = 130m/min&lt;/code&gt;. Für den &lt;code&gt;4mm&lt;/code&gt; Fräser ergibt sich&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;S = 10300U/min&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;F = 410mm/min&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Z+ = 3mm&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;vc = 1%&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Dieser Schaftfräser lebte etwa 1 Stunde lang. Nicht lange genug. Das Schneiden mit einem solchen Fräser könnte nie wirtschaftlich werden. Das HPL, das ich verwende, hat zwei Materialeigenschaften, die Hartmetallfräser schnell zerstören: Erstens ist es abrasiv, sodass die dünne Schneide des Fräsers abgetragen und zerstört wird. Meines ist ganz besonders abrasiv, da es einen unheimlich hohen Faseranteil besitzt. Zweitens leiten die Späne die entstehende Wärme nicht gut von der Schneide ab, so dass ihre Schärfe noch schneller nachlässt. Hitze scheint das Kryptonit für Hartmetallfräser zu sein.&lt;/p&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-18_coated_worn.jpg" alt="Image: coated carbide endmill for aluminium after 1h" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="endgültige-lösung"&gt;Endgültige Lösung&lt;/h3&gt;&#10;&lt;p&gt;Eine wirtschaftlichere Lösung mit 40-facher (Herstellerangabe) Lebensdauer wäre ein Hartmetallfräser mit Diamantschneiden (z. B. wie &lt;a href="https://www.artifex24.de/JSO-5mm-Diamant-PKD-Nutschaftfraeser-Diatec-DUO-5x10/50mm-Z2-S6-rechts" target="_blank" rel="noopener noreferrer" class="external-link"&gt;dieser hier&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;). Der Nachteil ist, dass sie fast zehnmal so teuer sind. Wenn ich dieses Material in Zukunft öfter schneiden muss, kaufe ich vielleicht einen und teile meine Erfahrungen - in der Zwischenzeit lasse ich die Fräser lieber schärfen, um Geld zu sparen.&lt;/p&gt;&#10;</description></item><item><title>CNC 'Opferplatte'</title><link>https://blog.schallbert.de/cnc-spoilboard-diy/</link><pubDate>Sun, 12 Jun 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cnc-spoilboard-diy/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/spoilboard/spoilboard-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Spoilboard for my BasicLine0607 CNC. It features a frame so pasts can be positioned repeatedly, and it has a hole grid to allow clamping devices to be inserted."&#10; title="CNC &amp;#39;Opferplatte&amp;#39;" /&gt;&#10;&lt;h2 id="materialliste"&gt;Materialliste&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Artikel&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Anzahl&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;~ Kosten [€]&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;MDF, Stärke 19mm, 840x730&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;30&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;MDF, Stärke 5mm, 840x730&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Einschlagmutter&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;17&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;9&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;M6 Maschinenschraube, 20mm + M6 Hammermutter + Unterlegscheibe&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;20&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;M6 Maschinenschraube, 35mm + Unterlegscheibe&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;17&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Holzleim&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;-&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,10&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="werkzeuge"&gt;Werkzeuge&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Schraubendreher / Schraubenschlüssel / Steckschlüssel&lt;/li&gt;&#10;&lt;li&gt;Ihre CNC&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="der-aufbau"&gt;Der Aufbau&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Laden Sie die Zeichnungen &lt;a href="https://blog.schallbert.de/assets/docs/basicline0607_spoilboard_drawings_by_schallbert.zip"&gt;als zip-Datei&lt;/a&gt; herunter&lt;/li&gt;&#10;&lt;li&gt;Passen Sie bei Bedarf die Abmessungen an &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&#10;&lt;li&gt;Importieren Sie die Zeichnung in Ihr CAM-Werkzeug. Passen Sie die Materialstärke an, falls Sie andere Platten verwenden als ich.&lt;/li&gt;&#10;&lt;li&gt;Bereiten Sie Senkbohrungen für Ihre Befestigungselemente vor, die am Maschinenrahmen befestigt werden. Die Tiefe dieser Bohrungen bestimmt die Reserve für das Planfräsen und damit das Aufbereiten verschlissener Oberflächen.&lt;/li&gt;&#10;&lt;li&gt;Da sowohl der Anschlag als auch Opferplatt größer sind als der von der CNC erreichbare Bereich, müssen Sie mehrere Jobs programmieren. Wählen Sie Ihre XY-Nullpunkte mit Bedacht und ziehen Sie gern die Projektseite zu Rate, auf der ich mein Vorgehen detailliert beschrieben habe (unten verlinkt).&lt;/li&gt;&#10;&lt;li&gt;Beachten Sie auch, dass die T-Nuten &amp;ldquo;von unten&amp;rdquo; gefräst werden müssen, falls Sie keinen Schlüssellochfräser haben.&lt;/li&gt;&#10;&lt;li&gt;Nachdem Ihre CNC die Aufträge abgearbeitet hat, kleben Sie die obere und untere MDF-Platte zusammen, damit die Hammermuttern nicht durch die Schlitze fallen können.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/projects/cnc_spoilboard/"&gt;Zurück zur Projekt-Hauptseite&lt;/a&gt;&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Hinweis: Die mittlere Versteifung des Maschinenrahmens hat keine festgelegte Position, sie kann bei Ihnen also an einer anderen Position stehen.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Mein Fräser 'kreischt'</title><link>https://blog.schallbert.de/my-endmill-screams/</link><pubDate>Mon, 06 Jun 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/my-endmill-screams/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-06_endmill-thumb.jpg"&#10; class="post-cover"&#10; alt="Anfängerproblem mit CNC: Vorschub/Dehzahl"&#10; title="Mein Fräser &amp;#39;kreischt&amp;#39;" /&gt;&#10;&lt;h3 id="einordnung"&gt;Einordnung&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-03_upcut_bit.jpg" alt="Image: a 2-flute upcut bit for wood" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich arbeite gerade an meinem allerersten CNC-Fräsprojekt: Eine &amp;ldquo;Opferplatte&amp;rdquo; mit Schraubmuffen und T-Nuten für die Niederhalter, welche einen Anschlag für Werkstücke und eine Aufnahme für den Werkzeuglängensensor besitzt.&lt;/p&gt;&#10;&lt;p&gt;Auf der Oberfräse habe ich bis jetzt nur gerade genutete HSS-Fräser mit großen Durchmessern verwendet. Für die CNC kaufte ich nun 6-mm-Vollhartmetallfräser &amp;ldquo;Fischschwanz&amp;rdquo; mit zwei Schneiden und einer Arbeitslänge von &lt;code&gt;21mm&lt;/code&gt;. Und der klingt einfach furchtbar, ganz anders als ich es von der Oberfräse gewöhnt bin.&lt;/p&gt;&#10;&lt;h3 id="die-verwirrung-des-anfängers"&gt;Die Verwirrung des Anfängers&lt;/h3&gt;&#10;&lt;p&gt;Mit diesem Fräser habe ich die vom Hersteller empfohlenen Vorschübe und Geschwindigkeiten für Hartholz auf meinem Werkstück aus mitteldichten Faserplatten eingehalten (Tiefe pro Durchgang: &lt;code&gt;6mm&lt;/code&gt;, Vorschub: &lt;code&gt;3500mm/min&lt;/code&gt;, Drehzahl: &lt;code&gt;24000U/min&lt;/code&gt;). Aber offensichtlich mache ich etwas falsch, denn beim Schneiden gibt es ein schrecklich lautes, unangenehmes schrilles und zischendes Geräusch.&lt;/p&gt;&#10;&lt;p&gt;Ich habe die Vorschubgeschwindigkeit allmählich erhöht, weil ich den Eindruck hatte, dass die Fräser wegen zu geringen Vorschubs so klingen. Ich landete bei &lt;code&gt;9100mm/min&lt;/code&gt; (!), wo es etwas besser war. Trotzdem ging dieses unangenehme Geräusch nicht weg.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-03-06_endmill_screams.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Fräser &amp;#39;kreischt&amp;#39; beim Zerspanen von MDF&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="analyse"&gt;Analyse&lt;/h3&gt;&#10;&lt;p&gt;Nachdem die 15-minütige Arbeit beendet war, habe ich den Schaftfräser angefasst - die Spannuten sind so gut wie neu und der Fräser fühlt sich kaum warm an. Hmm. Auch der Fräsmotor klang nicht gestresst. Meine Schrittmotoren ließen die Maschine buchstäblich durch das MDF pflügen (reibungslos und ohne Probleme), ich musste sogar die Absaugung aufdrehen, um nicht im Staub zu ertrinken. Auch das Ergebnis sieht für mich gut aus; die Toleranzen sind eng und der Schnitt ist so sauber, wie ich es erwarte - viel besser als ich es von Hand hätte machen können.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-03_mdf_result.jpg" alt="Image: milling in MDF - spoilboard t-nut slots" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="offene-fragen"&gt;Offene Fragen&lt;/h3&gt;&#10;&lt;p&gt;Was mache ich also falsch? Ist dieses Geräusch &amp;ldquo;normal&amp;rdquo; für einen 2-Schneider mit so kleinem Durchmesser? Braucht MDF ganz andere Parameter als Hartholz, um leise zu schneiden, z.B. viel weniger Spindeldrehzahl? Muss ich in meinem CAM-Tool eine andere Frässtrategie wählen?&lt;/p&gt;&#10;&lt;p&gt;Diese Fragen stelle ich schnell mal im Forum.&lt;/p&gt;&#10;&lt;h3 id="fragen-beantwortet"&gt;Fragen beantwortet&lt;/h3&gt;&#10;&lt;p&gt;OK, ich habe eine Antwort vom &lt;a href="https://hobbyline.info/forum/index.php?thread/620-mein-fr%C3%A4ser-kreischt/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Hersteller des Bits&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Dieses Geräusch ist normal für Schaftfräser, die auf das Schneiden von Holz spezialisiert sind. Nun denn. Wahrscheinlich sollte ich ein Gehäuse für meine Maschine bauen und ein paar sehr gute Ohrstöpsel kaufen. 😬 Nach ein paar Gesprächen mit einem weiteren Werkzeughersteller bestätigt sich diese Aussage nochmal:&lt;/p&gt;&#10;&lt;p&gt;&amp;ldquo;Zweischneider sind immer lauter als gleichgroße Ein- oder Dreischneider. Ihre im 180°-Winkel gegenüberliegenden Flanken sorgen durch die im Wechsel im Eingriff befindlichen Schneiden für zusätzliche Schwingungen des Fräsers. Dafür bieten sie ein besseres Verschleißverhalten als Einschneider und ermöglichen eine bessere Spanabfuhr als Dreischneider.&amp;rdquo;&lt;/p&gt;&#10;&lt;h3 id="vergleich-gleiches-material-anderer-fräser"&gt;Vergleich: Gleiches Material, anderer Fräser&lt;/h3&gt;&#10;&lt;p&gt;Hier ist ein Vergleichsvideo um zu beurteilen, wie unterschiedlich Fräser in demselben Material klingen können:&#10;&lt;code&gt;14mm 3-Schneider&lt;/code&gt; Planfräser, &lt;code&gt;10mm Schaft, S5500, F7100, Z0.3, Fxy 90%&lt;/code&gt;&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-03-06_leveling.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;14mm Planfräser nivelliert in aller Ruhe meine Opferplatte.&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Zum Testen habe ich in meinem CAM-Werkzeug eine Spiralstrategie verwendet, die 50% mehr Zeit benötigt als die typischen &amp;ldquo;Rasenmäher&amp;rdquo;-Strategien. Auf diese Weise gibt es keine sichtbaren Fräsbahnen im fertigen Material.&lt;/p&gt;&#10;&lt;h2 id="update-april-2024"&gt;Update April-2024:&lt;/h2&gt;&#10;&lt;p&gt;Nach etwa zwei Jahren Betrieb mit diesen Holzfräsern kann ich sagen, dass sie robust laufen und lange halten. Hier ist mehr Vorschub und mehr Tiefenzustellung besser, als zu behutsam vorzugehen (&lt;code&gt;F4000 S24000 Z+12mm&lt;/code&gt; ist in Birke Multiplex auf meiner Maschine durchaus drin). Und die Fräser sind im Gegenlauf deutlich leiser als im Gleichlauf.&lt;/p&gt;&#10;</description></item><item><title>CNC Teil5 - Makros</title><link>https://blog.schallbert.de/macros-for-cnc/</link><pubDate>Wed, 11 May 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/macros-for-cnc/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-11_sub-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: snippet of the tool length sensor self-test macro for my CNC machine. It shows C-style code and checks if the probe touches the sensor."&#10; title="CNC Teil5 - Makros" /&gt;&#10;&lt;p&gt;Dies ist der fünfte Teil meiner Serie über Portalfräsmaschinen. Er befasst sich mit praktischem Zubehör, welches das Leben als CNC-Bediener erleichtert und zu schnelleren und genaueren Fräsergebnissen führen kann.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/portal-milling/"&gt;&lt;em&gt;Teil 1&lt;/em&gt;: Gedanken zu CNCs im Allgemeinen und zur Maschinenauswahl&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cnc-electronics/"&gt;&lt;em&gt;Teil 2&lt;/em&gt;: CNC-Elektronik aufbauen&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/portal-milling-build/"&gt;&lt;em&gt;Teil 3:&lt;/em&gt; CNC-Maschinenbau&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/portal-milling-setup/"&gt;&lt;em&gt;Teil 4:&lt;/em&gt; CNC-Einrichtung&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="übersicht"&gt;Übersicht&lt;/h2&gt;&#10;&lt;p&gt;In diesem Artikel geht es um Makros für die CNC, mit deren Hilfe sich wiederholende Vorgänge automatisiert werden können. Ich decke sowohl grundlegende als auch anspruchsvollere Makros für mehrere Zwecke wie automatische Z-Nullstellung, Werkzeuglängenmessung oder Werkzeugwechsel ab.&#10;Ich führe ein paar &lt;a href="https://www.linuxcnc.org/docs/html/gcode/g-code.html#_g_code_quick_reference_table_a_id_gcode_quick_reference_table_a" target="_blank" rel="noopener noreferrer" class="external-link"&gt;G-Code&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Befehle ein. Die Beschäftigung damit half mir dabei, ein tieferes Verständnis der Funktionsweise zu erarbeiten und damit &amp;ldquo;Vertrauen&amp;rdquo; zu meiner Maschine aufzubauen.&lt;/p&gt;&#10;&lt;h2 id="haftungsausschluss"&gt;Haftungsausschluss&lt;/h2&gt;&#10;&lt;p&gt;Die folgenden Subroutinen wurden für den RS274 NGC Interpreter geschrieben. Ich habe sie mit &lt;a href="https://edingcnc.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;EdingCNC&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; validiert, da dies die von mir verwendete CNC-Software ist. Ich habe einige Aspekte von Makro-Dateien abgeleitet, die sowohl &lt;a href="https://webseite.sorotec.de/service/downloads/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Sorotec&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; als auch Eding zusammen mit ihren Maschinen ausliefern.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;⚠️ Ich kann für die Funktion des hier beschriebenen Codes auf anderen Maschinen keine Verantwortung übernehmen. Meine Absicht ist lediglich zu erklären wie die Makros funktionieren.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h2 id="was-ist-ein-makro"&gt;Was ist ein Makro?&lt;/h2&gt;&#10;&lt;p&gt;Ein Makro oder Unterprogramm ist eine Sammlung von Anweisungen, die für den CNC-Interpreter geschrieben wurden, um Aktionen auszuführen. Sie werden oft verwendet, um wiederkehrende Vorgänge zu automatisieren. Auf meiner Maschine beginnt ein solches Makro mit einem &lt;code&gt;SUB&lt;/code&gt;-Befehl und endet mit einem &lt;code&gt;ENDSUB&lt;/code&gt;. Makros enthalten oft Verzweigungslogik und G-Code-Befehle, beispielsweise um die Maschine Bewegungen ausführen zu lassen, Parameter abzuspeichern oder Benutzerdialoge anzuzeigen.&lt;/p&gt;&#10;&lt;p&gt;Auf meiner Maschine befinden sich alle Makros in einer einzigen Datei namens &lt;code&gt;macro.cnc&lt;/code&gt;, die von meiner CNC-Software beim Start eingelesen wird. Einige &amp;ldquo;Soft-Buttons&amp;rdquo; der Software rufen direkt Makros aus dieser Datei auf, z.B. für die Referenzfahrt.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-11_macro.jpg" alt="Image: Macro execution in action" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Als kleine Einführung öffne ich das Dokument &lt;code&gt;macro.cnc&lt;/code&gt; mit einem Texteditor. Ich suche nach &lt;code&gt;Sub user_9&lt;/code&gt; (es sollte eine Routine sein, die nichts Relevantes tut), entferne ihren Inhalt und füge stattdessen &lt;code&gt;MSG &amp;quot;hello world!&amp;quot;&lt;/code&gt; ein. Abspeichern, CNC-Software öffnen, die Taste &lt;code&gt;9&lt;/code&gt; im &lt;code&gt;user menu &lt;/code&gt; drücken und Voilà!&lt;/p&gt;&#10;&lt;h2 id="ein-einfaches-makro-zur-erkennung-des-status-des-werkzeuglängensensors"&gt;Ein einfaches Makro zur Erkennung des Status des Werkzeuglängensensors&lt;/h2&gt;&#10;&lt;p&gt;Legen wir los und verbinden das neu erworbene Wissen über Makros mit einem nützlichen Beispiel.&lt;/p&gt;&#10;&lt;h3 id="der-werkzeuglängensensor"&gt;Der Werkzeuglängensensor&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-11_tls3d.jpg" alt="Image: Mein Werkzeuglängensensor" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ein Werkzeuglängensensor (im Folgenden &lt;em&gt;WZLS&lt;/em&gt;) ist ein Zubehörteil, das an einen Eingang der CNC-Maschine angeschlossen wird und bei Berührung (z.B. durch eine Werkzeugspitze) auslöst. Es gibt viele verschiedene Ausführungen, von einem einfachen Mikroschalter mit Gehäuse und Tastmechanismus bis hin zu hochpräzisen induktiven Sensoren aus feingedrehtem Edelstahl mit automatischer Luftspülung um sicherzustellen, dass die Oberfläche des Sensors sauber ist.&lt;/p&gt;&#10;&lt;p&gt;Der Werkzeuglängensensor wird entweder am Maschinenbett, auf dem Ablagetisch oder einfach an einer anderen festen Position an der Maschine angebracht. Meiner ist ein sehr einfacher Schalter. Seine Wiederholgenauigkeit ist mit &lt;code&gt;10µm&lt;/code&gt; angegeben.&lt;/p&gt;&#10;&lt;h3 id="makro-aufgabe"&gt;Makro-Aufgabe&lt;/h3&gt;&#10;&lt;p&gt;Das Makro soll vor der Messung prüfen, ob der WZLS betriebsbereit ist. Ich teste also, ob der Sensorschalter den Status &amp;ldquo;nicht ausgelöst&amp;rdquo; hat. Seine Verdrahtung ist als &amp;ldquo;normally closed&amp;rdquo; (NC) ausgeführt. Falls er doch ausgelöst ist (seine Kontakte also offen sind), könnte das Folgendes bedeuten:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Der Sensor klemmt in der ausgelösten Position&lt;/li&gt;&#10;&lt;li&gt;Kabelbruch in der Zuleitung zum WZLS&lt;/li&gt;&#10;&lt;li&gt;Sensor ist nicht mit der Maschine verbunden&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="code-prüfe-betriebsbereitschaft-wzls"&gt;Code: Prüfe Betriebsbereitschaft WZLS&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SUB&lt;/span&gt; is_sensor_ok&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;sensorStatus &lt;span style="color:#f92672"&gt;==&lt;/span&gt; triggered&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# 5068 == 0 (normally closed)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;DLGMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Tool length sensor not connected or already triggered - please check&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;dialogButton &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;sensorStatus &lt;span style="color:#f92672"&gt;==&lt;/span&gt; triggered&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ERRMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Tool length sensor input still unexpected - aborting&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ERRMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;User abort.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ENDSUB&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Der Name des Makros lautet &lt;code&gt;is_sensor_ok&lt;/code&gt;. Ich prüfe den Sensorstatus und zeige eine Dialogmeldung an, falls hier etwas nicht stimmt. Sobald auf &amp;ldquo;OK&amp;rdquo; gedrückt wird, gehe ich davon aus, dass das Problem behoben wurde und versuche es erneut. Wenn er immer noch ausgelöst ist, breche ich die Routine ab.&lt;/p&gt;&#10;&lt;p&gt;Dieses Prüf-Makro ist in allen folgenden Abschnitten zu finden.&lt;/p&gt;&#10;&lt;h3 id="benennung-von-variablen"&gt;Benennung von Variablen&lt;/h3&gt;&#10;&lt;p&gt;Die RS274/NGC-Sprache ist alt. Wirklich alt. Ihre erste Version wurde in den späten 1950er Jahren veröffentlicht. Kein Wunder, dass die Parameter (#1 - #5399 ist der erlaubte Bereich) alle numerisch sind. Sowohl in der Benennung als auch in den Werten, die sie speichern können. Es gibt kein Typsystem (wie &lt;code&gt;Strings&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt; usw.) und keine praktischen Aliase oder menschenlesbare Namen, die das Verständnis eines Parameters wie &lt;code&gt;sensorStatus&lt;/code&gt; erleichtert.&lt;/p&gt;&#10;&lt;p&gt;Im Makro auf der Maschine wird stattdessen &lt;code&gt;#5068&lt;/code&gt; erscheinen man muss sich selbst daran erinnern, dass der Hersteller der CNC-Software such auf diese Variable festgelegt hat, um den Status des Werkzeuglängensensors zu kennzeichnen. Und dass sie vom Typ &lt;code&gt;boolean&lt;/code&gt; ist mit &lt;code&gt;0&lt;/code&gt; = nicht ausgelöst und &lt;code&gt;1&lt;/code&gt; = ausgelöst.&lt;/p&gt;&#10;&lt;p&gt;Um den Makrocode so lesbar und verständlich wie möglich zu gestalten, verzichte ich in diesem Artikel auf die Verwendung von numerischen Parameternamen. Eine Übersetzungstabelle ist &lt;a href="https://blog.schallbert.de/macros-for-cnc/#tables"&gt;ganz unten&lt;/a&gt; zu finden.&lt;/p&gt;&#10;&lt;h2 id="verwendung-des-werkzeuglängensensors-zur-ermittlung-der-werkstückhöhe-z0"&gt;Verwendung des Werkzeuglängensensors zur Ermittlung der Werkstückhöhe (Z=0)&lt;/h2&gt;&#10;&lt;p&gt;Hiermit kann die Werkstückhöhe automatisch eingemessen und auf der Obefläche abgenullt werden. So entfallen das manuelle Absenken der Z-Achse bis das Werkzeug die Oberfläche leicht ankratzt und dann das Festlegen der Werkstückkoordinaten.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-11_tls.jpg" alt="Image: Skizze Werkzeuglängensensor" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Sobald das Makro programmiert und eingerichtet ist, wird der WZLS möglichst mittig auf dem Werkstück platziert und die Spindel der Maschine mit ein paar Millimeter Luft direkt über diesem in Position gebracht. Dann wird das Makro gestartet. Die Maschine wird nun automatisch ihre Z-Achse langsam absenken, bis der Sensor schaltet. Dann fährt sie sehr langsam zurück, bis der Sensorkontakt wieder geschlossen ist. Dieser Punkt wird dann zur Bestimmung von &lt;code&gt;Z0&lt;/code&gt; herangezogen, welcher dann automatisch gesetzt wird.&lt;/p&gt;&#10;&lt;h3 id="voraussetzungen"&gt;Voraussetzungen&lt;/h3&gt;&#10;&lt;p&gt;Bevor wir das Makro schreiben können, benötigen wir die Z-Position des Werkzeuglängensensors &amp;ldquo;zTls&amp;rdquo; zum Zeitpunkt des Umschaltens von &amp;ldquo;ausgelöst&amp;rdquo; auf &amp;ldquo;nicht ausgelöst&amp;rdquo;. Ich habe meine vorerst (denn das ist nicht sehr genau) mit einem Messschieber gemessen und notiert. Wir brauchen diesen Wert, damit die Maschine ihn beim Ansetzen von der Z-Höhe abziehen kann, um die Position der Werkstückoberfläche zu ermitteln.&lt;/p&gt;&#10;&lt;p&gt;Wie man die Messung des Auslösepunktes präziser hinbekommt soll Gegenstand eines eigenen Artikels werden.&lt;/p&gt;&#10;&lt;p&gt;Außerdem ist es wichtig zu wissen, wie der Schalter mit der Maschine verbunden ist. Die bevorzugte Art ist &amp;ldquo;NC&amp;rdquo;, d.h. der Schalter öffnet sich, wenn er ausgelöst wird: &amp;ldquo;Ausgelöst = 0&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Schließlich müssen wir den Vorwärts- und Rückwärtsvorschub des Messtasters festlegen, z.B. &lt;code&gt;Absenken = 100mm/min&lt;/code&gt;, &lt;code&gt;Anheben = 10mm/min&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Das Makro enthält auch einen etwas aufwändigeren Teil: Wenn &lt;code&gt;Z0&lt;/code&gt; gemessen werden soll, obwohl die aktuell Werkzeuglänge noch unbekannt ist, macht das keinen Sinn: Bei einem Werkzeugwechsel wäre sie nicht in der Lage, den Nullabgleich auszuführen.&lt;/p&gt;&#10;&lt;p&gt;Daher wird in diesem Fall die Werkzeugvermessung zwischengeschoben: die Maschine speichert die aktuelle Position &lt;code&gt;posX, posY&lt;/code&gt; vor der Fahrt zum Werkzeuglängensensor in Hilfsvariablen zwischen und führt direkt das Makro &lt;code&gt;get_tool_length&lt;/code&gt; aus. Anschließend fährt sie wieder in die Ausgangsposition zurück, um die Vermessung von &lt;code&gt;Z0&lt;/code&gt; nachzuholen.&lt;/p&gt;&#10;&lt;h3 id="das-unterprogramm-z0-bestimmen"&gt;Das Unterprogramm &amp;ldquo;Z0 bestimmen&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Nach dem Ausschalten der Spindel senkt das Programm mit dem Befehl &lt;code&gt;G38.2&lt;/code&gt; die Z-Achse der Maschine ab, bis der Fräser Werkzeuglängensensor auslöst (oder bis sie ohne eingelegtes Werkzeug so weit nach unten gefahren ist, dass die Spindelnase den Sensor fast berührt, wo das Makro dann wiederum abbrechen würde, weil es den Sensor nicht gefunden hat). Sobald der Sensor ausgelöst, fährt die Maschine vorsichtig die Z-Achse wieder hoch. Der Punkt der erneuten Freigabe des Sensors durch das Werkzeug wird dann als Koordinaten-Offset für die Z-Achse mit dem Befehl &lt;code&gt;G92&lt;/code&gt; gespeichert.&lt;/p&gt;&#10;&lt;h3 id="code-werkstücknullpunkt-bestimmen"&gt;Code: Werkstücknullpunkt bestimmen&lt;/h3&gt;&#10;&lt;p&gt;Bevor wir beginnen stellen wir sicher, dass alle Vorbedingungen erfüllt sind:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Die Werkzeuglänge ist bekannt&lt;/li&gt;&#10;&lt;li&gt;Der WZLS ist korrekt angeschlossen&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SUB&lt;/span&gt; measure_workpiece_z0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;![&lt;/span&gt;toolLengthStat&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;DLGMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;WARNING - Please first measure tool length!&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;dialogButton &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; xPosReposition &lt;span style="color:#f92672"&gt;=&lt;/span&gt; xPos&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; yPosReposition &lt;span style="color:#f92672"&gt;=&lt;/span&gt; yPos&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; repositionTo &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;GOSUB&lt;/span&gt; get_tool_length&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;GOSUB&lt;/span&gt; is_sensor_ok ;&lt;span style="color:#75715e"&gt;# Check if tool length sensor status is OK&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Programmieren wir nun die eigentliche Routine, indem wir abfragen, ob der Z-Nullabgleich jetzt durchgeführt werden soll. Wenn die CNC im Simulationsmodus läuft (ohne angeschlossene Hardware), wird der Z-Nullabgleich nicht funktionieren. Also müssen wir auch dies überprüfen und den Schritt gegebenenfalls überspringen.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;DLGMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Start Z-Zeroing?&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;dialogButton &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;AND&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;operatingMode &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; simulator&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;M5&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;#Switch spindle off&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;M9&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;#Switch coolant off&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G38&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zSpindleTip &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; F&lt;span style="color:#f92672"&gt;[&lt;/span&gt;touch&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ;&lt;span style="color:#75715e"&gt;# G38.2 = touch toward probe, stop on contact, flag error on fail&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ;&lt;span style="color:#75715e"&gt;# fail = 5mm before touching spindle tip&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;probeOk &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# #5067 == 1 : G38.2 command success&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G38&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G91&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Z20&lt;/span&gt; F&lt;span style="color:#f92672"&gt;[&lt;/span&gt;rev&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# now reverse slowly to find untrigger point (max. 20mm up)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G90&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# back to absolute coordinates&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;probeOk &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zTouched&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# #5063 Go to Z-axis&amp;#39;s probe point&#9;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G92&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zTls&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Set Z-axis&amp;#39;s coordinate offset (0) to tool length sensor&amp;#39;s height&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zTls &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# clear sensor&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ERRMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Could not locate sensor untrigger point.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;DLGMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Could not locate sensor trigger point. Retry?&amp;#34;&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;dialogButton &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &#9;&#9;&#9;&#9;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;GOSUB&lt;/span&gt; measure_workpiece_z0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ERRMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;User abort.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ENDSUB&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-06-11_z_zeroing.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Ausführen des Makros Z-Nullabgleich auf Zerspanobert&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="messung-der-werkzeuglänge"&gt;Messung der Werkzeuglänge&lt;/h2&gt;&#10;&lt;p&gt;Wenn die Maschine die Werkzeuglänge kennt, bevor sie den Z-Nullpunkt misst, muss der Z-Nullpunkt bei anstehenden Werkzeugwechseln während des Auftrags nicht erneut bestimmt werden. Das passende Makro beschreibe ich im Folgenden.&lt;/p&gt;&#10;&lt;h3 id="vorbereitung"&gt;Vorbereitung&lt;/h3&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-06-11_toolLengthVariables.jpg" alt="Image: get_tool_length macro parameters" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die meisten CNCs leiten ihre aktuellen Positionsdaten durch Zählen der Motorschritte ab, die sie nach Abschluss der Referenzfahrt ausgeführt hat. Um nun die Werkzeuglänge zu vermessen, benötigt sie ein paar einprogrammierte Parameter:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Z-Position der Spindelnase &lt;code&gt;zSpindleTip&lt;/code&gt; (berührt Werkzeuglängensensor ohne eingelegtes Werkzeug) damit berechnet werden kann, wie weit das Werkzeug aus der Spindel herausragt&lt;/li&gt;&#10;&lt;li&gt;X-Position &lt;code&gt;xPosTls&lt;/code&gt; des Werkzeuglängensensors beim Werkzeugwechsel&lt;/li&gt;&#10;&lt;li&gt;Y-Position &lt;code&gt;yPosTls&lt;/code&gt; des Werkzeuglängensensors bei Werkzeugwechseln&lt;/li&gt;&#10;&lt;li&gt;Sicherheitshöhe &lt;code&gt;zSafety&lt;/code&gt; (stellt sicher, dass die Spindel auf ihrem Weg zum Werkzeugwechselbereich nicht auf ein Hindernis stößt)&lt;/li&gt;&#10;&lt;li&gt;Tastvorschub vorwärts und rückwärts. Wir können ihn aus dem Makro für den Werkstücknullpunkt wiederverwenden.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="die-unterroutine-werkzeuglänge-messen"&gt;Die Unterroutine &amp;ldquo;Werkzeuglänge messen&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Legen wir zunächst fest, was diese Unterroutine tun soll:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Spindel, Kühlmittel etc. anhalten.&lt;/li&gt;&#10;&lt;li&gt;Dialog: Bediener soll geschätzte Werkzeuglänge eingeben&lt;/li&gt;&#10;&lt;li&gt;Eilgang Z auf Sicherheitshöhe, dann XY zur Position des Werkzeugsensors&lt;/li&gt;&#10;&lt;li&gt;Werkzeuglängensensor antasten, ähnlich wie bei der Werkstücknullpunktbestimmung&lt;/li&gt;&#10;&lt;li&gt;Werkzeuglänge berechnen&lt;/li&gt;&#10;&lt;li&gt;Berechnung der Differenz zwischen der letzten und der aktuellen Werkzeuglänge&lt;/li&gt;&#10;&lt;li&gt;Z-Nullpunkt entsprechend aktualisieren&lt;/li&gt;&#10;&lt;li&gt;Z im Eilgang wieder auf Sicherheitshöhe bringen&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="function-measure-tool-length"&gt;Code: Werkzeug vermessen&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SUB&lt;/span&gt; get_tool_length&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;GOSUB&lt;/span&gt; is_sensor_ok ;&lt;span style="color:#75715e"&gt;# Check if tool length sensor status is OK&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;DLGMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Start tool length measurement? Please enter estimated tool length: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;toolLengthEst&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;dialogButton &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;AND&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;operatingMode &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; simulator&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;toolLengthEst &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ERRMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Error: tool length cannot be negative.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;zSpindleTip&lt;span style="color:#f92672"&gt;+&lt;/span&gt; toolLengthEst &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;zSafety&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ERRMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Error: tool too long - could collide with sensor.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;M5&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Switch spindle off&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;M9&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Switch coolant off&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zSafety&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Go to safety height (machine coordinates)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; X&lt;span style="color:#f92672"&gt;[&lt;/span&gt;xPosTls&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Y&lt;span style="color:#f92672"&gt;[&lt;/span&gt;xPosTls&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Go to tool length sensor&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zSpindleTip &lt;span style="color:#f92672"&gt;+&lt;/span&gt; toolLengthEst &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Move Z down to 10mm above estimated tool tip&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ;&lt;span style="color:#75715e"&gt;# measure tool length, save results, apply Z-offset if needed&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G38&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zSpindleTip&lt;span style="color:#f92672"&gt;]&lt;/span&gt; F&lt;span style="color:#f92672"&gt;[&lt;/span&gt;touch&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# probe sensor, latest stop point is spindle tip&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;probeOk &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# #5067 == 1 : G38.2 command success&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G38&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G91&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Z20&lt;/span&gt; F&lt;span style="color:#f92672"&gt;[&lt;/span&gt;rev&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# now reverse slowly to find untrigger point&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G90&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# back to absolute coordinates&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;probeOk &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toolLength &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;zTouched &lt;span style="color:#f92672"&gt;-&lt;/span&gt; sTip&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;MSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Tool length = &amp;#34;&lt;/span&gt; toolLength&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;toolLengthStat &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# defaults to 0 on startup&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastToolLength &lt;span style="color:#f92672"&gt;=&lt;/span&gt; currToolLength ;&lt;span style="color:#75715e"&gt;# save last tool&amp;#39;s length&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; currToolLength &lt;span style="color:#f92672"&gt;=&lt;/span&gt; toolLength ;&lt;span style="color:#75715e"&gt;# save current tool&amp;#39;s length&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toolLengthDiff &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;currToolLength &lt;span style="color:#f92672"&gt;-&lt;/span&gt; lastToolLength&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#9;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G92&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zPos &lt;span style="color:#f92672"&gt;-&lt;/span&gt; toolLengthDiff&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Set Z-axis&amp;#39;s coordinate offset (0)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; currToolLength &lt;span style="color:#f92672"&gt;=&lt;/span&gt; toolLength ;&lt;span style="color:#75715e"&gt;# save current tool&amp;#39;s length&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toolLengthStat &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;GOSUB&lt;/span&gt; reposition_spindle&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ERRMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Could not locate sensor untrigger point.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ERRMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Could not locate sensor trigger point.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ENDSUB&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-06-11_tool_length_measurement.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Werkzeuglängenmakro auf meiner Maschine&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;In diesem Video ist die Maschine so konfiguriert, dass sie zum XY-Nullpunkt zurückkehrt, wenn die Werkzeugmessung abgeschlossen ist.&lt;/p&gt;&#10;&lt;h2 id="werkzeugwechsel"&gt;Werkzeugwechsel&lt;/h2&gt;&#10;&lt;p&gt;Wenn es kein Makro für den Werkzeugwechsel gibt, hält die Maschine ihren Auftrag an. Sobald die &lt;code&gt;Z0&lt;/code&gt;-Position des Werkstücks erneut ermittelt wurde (denn die Werkzeuglänge hat sich ja wahrscheinlich geändert), kann der Auftrag fortgesetzt werden.&lt;/p&gt;&#10;&lt;p&gt;Diese Aufgabe kann automatisiert werden, indem der Z-Nullpunkt durch Bestimmen der neuen Werkzeuglänge gegenüber der alten neu kalibriert wird.&lt;/p&gt;&#10;&lt;h3 id="voraussetzungen-1"&gt;Voraussetzungen&lt;/h3&gt;&#10;&lt;p&gt;Ein optionales Flag &lt;code&gt;getToolLength&lt;/code&gt; kann das Verhalten des Werkzeugwechsel-Makros konfigurieren - ob die Länge jedes Werkzeugs nach einem Werkzeugwechsel gemessen werden soll oder nicht. Wenn Sie einen automatischen Werkzeugwechsler haben, brauchen Sie dies wahrscheinlich nicht: In diesem Falle ist die Werkzeuglänge in einer Tabelle festgehalten und die Längenkompensation erfolgt ohne Vermessung.&lt;/p&gt;&#10;&lt;p&gt;Die folgenden zusätzlichen Parameter werden ebenfalls benötigt:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;X-Position &lt;code&gt;xToolChg&lt;/code&gt; wo der Werkzeugwechsel stattfindet (bei mir ist es &lt;code&gt;xToolChg = xPosTls&lt;/code&gt;)&lt;/li&gt;&#10;&lt;li&gt;Y-Position &lt;code&gt;yToolChg&lt;/code&gt;, wo der Werkzeugwechsel stattfindet (bei mir ist es &lt;code&gt;yToolChg = yPosTls&lt;/code&gt;)&lt;/li&gt;&#10;&lt;li&gt;Wiederverwendung von &lt;code&gt;zSafety&lt;/code&gt; aus &lt;code&gt;get_tool_length&lt;/code&gt; Makro&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;newToolNumber&lt;/code&gt; zur Angabe des angeforderten Werkzeugs aus dem G-Code (oder bei manueller Eingabe)&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;currToolNumber&lt;/code&gt; zur Angabe des &amp;ldquo;alten&amp;rdquo; Werkzeugs, das ersetzt werden soll&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;ToolChangeDone&lt;/code&gt;-Hilfsflag, um anzuzeigen, ob ein Werkzeugwechsel bereits stattgefunden hat&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="das-makro-werkzeugwechsel"&gt;Das Makro &amp;ldquo;Werkzeugwechsel&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Die folgenden Schritte werden von dem Makro ausgeführt, wenn ein Werkzeugwechsel entweder durch den Befehl &lt;code&gt;M06&lt;/code&gt; &amp;ldquo;Werkzeugwechsel&amp;rdquo; in der G-Code-Datei eines Jobs oder manuell durch Benutzer ausgelöst wird:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Spindel, Kühlmittel etc. anhalten.&lt;/li&gt;&#10;&lt;li&gt;Wenn das angeforderte Werkzeug das aktuelle Werkzeug ist, erscheint ein Dialog mit der Frage, ob es trotzdem gewechselt werden soll.&lt;/li&gt;&#10;&lt;li&gt;Z im Eilgang nach oben (&lt;code&gt;zSafety&lt;/code&gt;), dann XY auf die Werkzeugwechselposition fahren&lt;/li&gt;&#10;&lt;li&gt;Dialog: Angabe des aktuellen Werkzeugs &lt;code&gt;a&lt;/code&gt; und Aufforderung, das gewünschte Werkzeug &lt;code&gt;b&lt;/code&gt; einzusetzen&lt;/li&gt;&#10;&lt;li&gt;Prüfen, ob die Werkzeugwechselkonfiguration die Bestimmung der Werkzeuglänge impliziert. Wenn ja, Makro &amp;ldquo;get_tool_length&amp;rdquo; aufrufen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="code"&gt;Code&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SUB&lt;/span&gt; change_tool&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toolChangeDone &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;M5&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Switch spindle off&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;M9&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Switch coolant off&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;operatingMode &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; simulator&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;TCAGuard&lt;/span&gt; off ;&lt;span style="color:#75715e"&gt;# tool change area guard: off for tool change&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ;&lt;span style="color:#75715e"&gt;# handle case that tool is already in place&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;newToolNumber &lt;span style="color:#f92672"&gt;==&lt;/span&gt; currToolNumber&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;DLGMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Tool already mounted. Change anyways?&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;dialogButton &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toolChangeDone &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toolChangeDone &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ;&lt;span style="color:#75715e"&gt;# go to tool change position and prompt to change tool&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;toolChangeDone &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zSafety&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Go to safety height (machine coordinates)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; X&lt;span style="color:#f92672"&gt;[&lt;/span&gt;xToolChg&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Y&lt;span style="color:#f92672"&gt;[&lt;/span&gt;yToolChg&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Go to tool change position&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;DLGMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Please mount tool now. Old tool number: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;currToolNumber&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34; New tool number: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;newToolNumber&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;dialogButton &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;newToolNumber &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;99&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;OR&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;newToolNumber &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;TCAGuard&lt;/span&gt; on ;&lt;span style="color:#75715e"&gt;# tool change area guard: on for normal job&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ERRMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;New tool number implausible.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toolChangeDone &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ERRMSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Tool change aborted.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ;&lt;span style="color:#75715e"&gt;# prompt when complete and optionally call tool length measurement&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;toolChangeDone &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;MSG&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Tool change from &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;currToolNumber&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34; to &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;newToolNumber&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34; complete.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;M6&lt;/span&gt; T&lt;span style="color:#f92672"&gt;[&lt;/span&gt;newToolNumber&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#9;&#9;&#9;&#9; ;&lt;span style="color:#75715e"&gt;# set new tool number&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;getToolLength &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# config flag 0 = no, 1 = yes&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;GOSUB&lt;/span&gt; get_tool_length ;&lt;span style="color:#75715e"&gt;# Measure tool length. Careful: To be called after M6 T !&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;GOSUB&lt;/span&gt; reposition_spindle&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;TCAGuard&lt;/span&gt; on ;&lt;span style="color:#75715e"&gt;# tool change area guard: on for normal job&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ENDSUB&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="repositionieren-auf-gespeicherte-koordinaten"&gt;Repositionieren (auf gespeicherte Koordinaten)&lt;/h2&gt;&#10;&lt;p&gt;Dieses kurze Makro wird von anderen Unterprogrammen aufgerufen, um die Maschine entweder zu einer gewünschten Position oder zum XY-Werkstücknullpunkt zu fahren. Es kann mit mehreren &lt;code&gt;repositionTo&lt;/code&gt;-Flag-Werten erweitert werden, z.B.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-log" data-lang="log"&gt;0 = keine_Neupositionierung, 1 = benutzerdefinierte_Position, 2 = Werkstücknullpunkt, 3 = Maschinennullpunkt ...&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="code-1"&gt;Code&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SUB&lt;/span&gt; reposition_spindle&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G53&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; Z&lt;span style="color:#f92672"&gt;[&lt;/span&gt;zSafety&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Go to safety height (machine coordinates)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;repositionTo &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; X&lt;span style="color:#f92672"&gt;[&lt;/span&gt;xPosReposition&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Y&lt;span style="color:#f92672"&gt;[&lt;/span&gt;yPosReposition&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Move back to where requested before&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; repositionTo &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# reset reposition flag and values&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; xPosReposition &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; yPosReposition &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ELSE&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;G00&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;X0&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Y0&lt;/span&gt; ;&lt;span style="color:#75715e"&gt;# Move back to XY zero (workpiece coordinates)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ENDIF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ENDSUB&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="tables"&gt;Tabelle der Variablen&lt;/h2&gt;&#10;&lt;p&gt;Typ-Erläuterungen:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;protected&lt;/code&gt;: Parameter gehört zu festen Befehlen oder Zuständen, die schreibgeschützt sind&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;reserved&lt;/code&gt; : Parameter hat eine feste Verwendung innerhalb meiner CNC-Software&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;free&lt;/code&gt; : Parameter kann frei durch den Programmierer verwendet werden (Ist nach Not-Aus oder Neustart aber zurückgesetzt!)&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;persisted&lt;/code&gt;: Parameter wird im nicht-flüchtigen Speicher abgelegt&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="systemparameter"&gt;Systemparameter&lt;/h3&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Variable name&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Parameter nr.&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Type&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Comment&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;xPos&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5001&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;protected&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;current CNC position&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;yPos&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5002&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;protected&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;current CNC position&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;zPos&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5003&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;protected&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;current CNC position&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;currToolNumber&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5008&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;protected&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;[1&amp;hellip;99]&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;newToolNumber&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5011&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;protected&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;[1&amp;hellip;99]&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;zTouched&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5063&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;protected&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Z where sensor touched&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;probeOk&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5067&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;protected&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1 = OK&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;sensorStatus&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5068&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;protected&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1 = triggered&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;operatingMode&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5397&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;reserved&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1 = simulator&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;dialogButton&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5398&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;reserved&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1 = OK&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="konfig-parameter"&gt;Konfig-Parameter&lt;/h3&gt;&#10;&lt;p&gt;Config-Parameter sind die Parameter, die einmal eingestellt und dann konstant gehalten werden, da sie an die CNC und ihre Geometrie gebunden sind.&lt;/p&gt;&#10;&lt;p&gt;Die Volatilität der Parameter hängt stark von der CNC-Softwarelösung ab, die Sie verwenden, und die Anzahl der freien Parameter kann unterschiedlich sein. In meiner Software werden die Variablen im Bereich &amp;ldquo;#4000 - #4999&amp;rdquo; beibehalten, während alle anderen freien und reservierten Parameter flüchtig und/oder skaliert sind.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Variable name&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Parameter nr.&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Type&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Comment&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;triggered&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4400&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1 = normally open&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;currToolLength&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4501&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;[mm]&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;lastToolLength&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4502&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;[mm]&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;zSafety&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4506&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Z position for move&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;xPosTls&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4507&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;tls position&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;xPosTls&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4508&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;tls position&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;zSpindleTip&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4509&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Z zpindle on tls&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;zTls&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4510&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;TLS height [mm]&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;toolLengthEst&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4511&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;[mm]&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;touch&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4512&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;touch feed [mm/min]&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;rev&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4513&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;reverse feed [mm/min]&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;getToolLength&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4520&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Flag, 1 = Yes&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;xToolChg&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4521&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;tool change position&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;yToolChg&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#4522&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free, persisted&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;tool change position&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="flag-parameter"&gt;Flag-Parameter&lt;/h3&gt;&#10;&lt;p&gt;Freie Parameter können nach Belieben verwendet werden. Es muss jedoch darauf geachtet werden, dass nicht versehentlich eine bestehende Systemvariable wiederverwendet oder überschrieben wird.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Variable name&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Parameter nr.&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Type&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Comment&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;toolLengthStat&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#3501&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1 = measured&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;toolLengthDiff&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#3502&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;[mm]&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;toolChangeDone&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5015&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1 = Yes&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;repositionTo&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5020&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Flag, 1 = Yes&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;xPosReposition&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5021&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;reposition flag&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;yPosReposition&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5022&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;reposition flag&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;toolLength&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;#5024&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;[mm]&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;PS: Damit die obigen Code-Beispiele ordentlich angezeigt werden, habe ich fürs Markup die Sprachinterpretation &lt;code&gt;ruby&lt;/code&gt; verwendet. Hier kommt die farbige Syntax-Markierung am besten überein mit der Logik des NGC-interpreters.&lt;/p&gt;&#10;</description></item><item><title>CNC Teil 4.1 - Konfiguration</title><link>https://blog.schallbert.de/edingcnc-config/</link><pubDate>Tue, 08 Mar 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/edingcnc-config/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-08_edingcnc-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Zerspanobert&amp;#39;&amp;#39;s switchbox, components labeled"&#10; title="CNC Teil 4.1 - Konfiguration" /&gt;&#10;&lt;p&gt;Dies ist Teil 4.1 meiner Reihe über Portalfräsmaschinen. Er hängt eng zusammen mit &lt;a href="https://blog.schallbert.de/portal-milling-setup/"&gt;Teil4&lt;/a&gt;, konzentriert sich aber auf die korrekte Einstellung meiner Steuersoftware für die Fräse.&lt;/p&gt;&#10;&lt;h2 id="hardware"&gt;Hardware&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://edingcnc.com/product/cpu5a4e/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;EdingCnc V5A-4E&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Prozessorboard oder ähnlich&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.benezan-electronics.de/index.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Interface Advanced PRO&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Breakout board oder ähnlich&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.upload.sorotec.de/doku/manuals/adapter.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Adapter Board&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; falls nötig zum Verbinden von Prozessor- und Breakout board&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Diese Komponenten sind notwendig, um die von der CNC-Software erzeugten Befehle in Signale der physischen Welt zu übersetzen, die mit Materie interagieren können. Die Breakout-Platine wird vom CNC-Prozessor gesteuert und bietet Relaisausgänge für die Oberfräse und meinen Staubabscheider, so dass ich sie über den Laptop einschalten kann. Es gibt auch analoge Spannungsausgänge, z.B. zur Steuerung der Spindeldrehzahl, und mehrere Eingänge, z.B. zum Ablesen des Referenzschalterstatus und zur Rückmeldung von Ereignissen an die CNC-Software.&lt;/p&gt;&#10;&lt;p&gt;Natürlich gibt es viele Hersteller für diese Schaltkreise. Ich verwende das oben genannte Ökosystem, so dass ich nur für dieses sprechen kann. Die zugrundeliegende Technologie ist stets ähnlich oder sogar identisch, und obwohl Sie vielleicht eine andere Benutzeroberfläche oder einen anderen Kommunikationskanal wie USB haben, wird Ihre Software wahrscheinlich ähnliche Einstellungen bieten.&lt;/p&gt;&#10;&lt;h2 id="setup-bildschirm-1"&gt;Setup Bildschirm 1&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-08_settings1.jpg" alt="Image: EdingCNC settings screen1" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="kinematik"&gt;Kinematik&lt;/h3&gt;&#10;&lt;p&gt;Wie bereits ausführlich &lt;a href="https://blog.schallbert.de/portal-milling-setup/#kinematics"&gt;in diesem Beitrag&lt;/a&gt; beschrieben, zeigt der grüne Rahmen die Kinematikeinstellungen. Ein negatives Vorzeichen in &lt;code&gt;Schritte/[mm]&lt;/code&gt; bedeutet, dass die Richtung des Schrittmotors invertiert ist. Zu beachten ist, dass das Feld &amp;ldquo;Umkehrspiel&amp;rdquo; 0 bleiben kann, wenn Kugelumlaufspindeln verwendet werden. Ich verwende &lt;code&gt;einfache Kinematik&lt;/code&gt;, da ich keine Drehachse besitze. Also ist das Koordinatensystem kartesisch.&lt;/p&gt;&#10;&lt;h3 id="referenz--notaus"&gt;Referenz / Notaus&lt;/h3&gt;&#10;&lt;p&gt;Dieser Abschnitt steuert, wie die Software die Signalpegel für die Eingangsschalter auf der Schnittstellenkarte interpretiert. Obwohl ich sicher bin, dass ich alle Eingänge &amp;ldquo;normal geschlossen&amp;rdquo; verdrahtet habe, um einen unentdeckten Kabelbruch zu vermeiden, musste ich den Eingang für den Notschalter invertieren.&lt;/p&gt;&#10;&lt;h3 id="spindel"&gt;Spindel&lt;/h3&gt;&#10;&lt;p&gt;Diese Einstellungen sind sehr spezifisch für die Maschine, die Sie verwenden. Ich wollte, dass die Spindel anhält, wenn ich einen Auftrag unterbreche, und dass sie automatisch wieder anläuft, bevor ich den Auftrag fortsetze.&lt;/p&gt;&#10;&lt;p&gt;Der Parameter &lt;code&gt;ApproachFeed&lt;/code&gt; ist ein wenig verwirrend, weil der Standardwert hier sehr niedrig ist. Seine Bedeutung ist die folgende: Er gibt den Vorschub der Z-Achse nach unten an, wenn ein Auftrag angehalten und die Z-Achse zuvor aufwärts bewegt wurde. Wenn Sie den Auftrag fortsetzen, wird die Z-Achse mit diesem Vorschub abgesenkt, und wenn die Zieltiefe erreicht ist, wird der Auftrag normal fortgesetzt. Ich habe &lt;code&gt;600mm/min&lt;/code&gt; als einen Wert eingegeben, den die meisten Schaftfräser bewältigen können sollten.&lt;/p&gt;&#10;&lt;h2 id="setup-bildschirm-2"&gt;Setup Bildschirm 2&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-08_settings2.jpg" alt="Image: EdingCNC settings screen2" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Viele Einstellungen hier lasse ich (vorerst) bei ihren Standardwerten. Auf meine Anpassungen gehe ich weiter unten ein.&lt;/p&gt;&#10;&lt;h3 id="benutzeroberfläche"&gt;Benutzeroberfläche&lt;/h3&gt;&#10;&lt;p&gt;Ich wollte, dass das Referenzieren der Maschine Pflicht ist, bevor ich einen Auftrag laden oder die Achsen der Maschine im Eilgang verfahren kann.&#10;Außerdem musste ich &amp;ldquo;automatischer Werkzeugwechsel&amp;rdquo; anwählen, obwohl ich keinen Werkzeugwechsler habe: sonst würde mein Makro für den manuellen Werkzeugwechsel nicht aufgerufen werden.&lt;/p&gt;&#10;&lt;h3 id="schnittstelle-ea"&gt;Schnittstelle E/A&lt;/h3&gt;&#10;&lt;p&gt;Um die Hardware-Einstellungen des Breakout-Boards zu berücksichtigen, musste ich einige Software-Ausgänge invertieren. Wenn ich z.B. &lt;code&gt;PWM1&lt;/code&gt; nicht invertiert hätte, würde die Spindel mit voller Drehzahl starten, obwohl ich nur &lt;code&gt;5000rpm&lt;/code&gt; wollte und umgekehrt. Dies liegt an teils unterschiedlicher Interpretation der Logikpegel, welche als &amp;ldquo;open collector&amp;rdquo; oder auch &amp;ldquo;open emitter&amp;rdquo; ausgelegt sein können.&lt;/p&gt;&#10;&lt;h2 id="variablen-bildschirm"&gt;Variablen-Bildschirm&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-08_variables.jpg" alt="IMage: EdingCNC variable screen" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="parkposition-g28--g30"&gt;Parkposition G28 / G30&lt;/h3&gt;&#10;&lt;p&gt;Ich stelle meine Parkpositionen so ein, dass ich effizient arbeiten kann. Einer dieser Befehle bewirkt, dass die Achsen auf den XY-Nullpunkt meines Anschlags fahren, den ich auch in CAM zum Werkstück-Nullpunkt mache, so dass das Einnullen wirklich einfach wird.&lt;/p&gt;&#10;&lt;p&gt;Die andere Parkposition wird auf einen Punkt gesetzt, an dem die Referenzschalter aller drei Achsen ganz nah sind, so dass das Referenzieren der Maschine nach dem Hochfahren sehr schnell abläuft.&lt;/p&gt;&#10;&lt;h3 id="überwachung-der-variablen"&gt;Überwachung der Variablen&lt;/h3&gt;&#10;&lt;p&gt;Schauen wir uns das Überwachungsfenster an. Hier können Sie eine Variable aus dem Interpreterbereich der &lt;code&gt;RS274 NGC&lt;/code&gt;eingeben und sich deren aktuelle Werte anzeigen lassen. Dies ist praktisch, wenn Sie Makros debuggen oder einfach nur interne Parameter wie die gemessene Werkzeuglänge, die Lage des aktuellen Z-Nullpunkts etc. ablesen wollen.&lt;/p&gt;&#10;</description></item><item><title>CNC Teil4 - Setup</title><link>https://blog.schallbert.de/portal-milling-setup/</link><pubDate>Thu, 03 Mar 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/portal-milling-setup/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-03_setup-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: First steps with CNC software &amp; config"&#10; title="CNC Teil4 - Setup" /&gt;&#10;&lt;p&gt;Dies ist der vierte Teil meiner Reihe über Portalfräsmaschinen. Er konzentriert sich auf das Zusammenspiel von Hardware und Software, sodass mein fertig aufgebauter &amp;ldquo;Zerspanobert&amp;rdquo; bald im Produktivbetrieb arbeiten kann.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/portal-milling/"&gt;&lt;em&gt;Teil 1&lt;/em&gt;: Gedanken zu Portalfräsmaschinen und ihre Auslegung für den (ambitionierten) Hobbybereich&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cnc-electronics/"&gt;&lt;em&gt;Teil 2&lt;/em&gt;: CNC Elektrik / Elektronik&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/portal-milling-build/"&gt;&lt;em&gt;Teil 3:&lt;/em&gt; Zusammenbau&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/edingcnc-config/"&gt;&lt;em&gt;Teil 4.1:&lt;/em&gt; Konfiguration der Software&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/macros-for-cnc/"&gt;&lt;em&gt;Teil 5:&lt;/em&gt; Makros für die CNC&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="kinematics"&gt;Kinematik&lt;/h2&gt;&#10;&lt;p&gt;Bevor die CNC-Software auf meiner &lt;strong&gt;BasicLine 0607&lt;/strong&gt; arbeiten kann, muss sie korrekt eingerichtet werden. Sie muss die Verfahrbereiche der Maschinenachsen kennen und wissen, wo sich die Referenzschalter befinden. Außerdem ist relevant, in welche Richtung die Schrittmotoren gedreht werden müssen, um eine &amp;ldquo;positive/negative&amp;rdquo; Achsenbewegung zu erreichen.&lt;/p&gt;&#10;&lt;p&gt;In einem ersten Schritt muss entschieden werden, wie die Achsen der Maschine benannt werden (X, Y, Z), wo alle Achsen tatsächlich 0 sein sollen, und in welche Richtung die Bewegung gehen soll (positiv, negativ). Alle diese Koordinatenpunkte sind relativ und können willkürlich gewählt werden, aber es gibt einige Konventionen in der CNC-Welt:&lt;/p&gt;&#10;&lt;p&gt;Achsen:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Die X-Achse liegt auf dem Portalbett und bewegt die Y-Achse des Portals.&lt;/li&gt;&#10;&lt;li&gt;Die Y-Achse befindet sich oben auf dem Portal und bewegt die Z-Achsen-Baugruppe.&lt;/li&gt;&#10;&lt;li&gt;Die Z-Achse befindet sich an der Spindel und bewegt diese auf- und abwärts.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="maschinen-nullpunkt"&gt;Maschinen-Nullpunkt&lt;/h3&gt;&#10;&lt;p&gt;Maschinen- Nullpunkt und -Bewegungsrichtung:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;X-0 befindet sich auf der &amp;ldquo;hinteren&amp;rdquo; Seite der Achse. Die Bewegungsrichtung ist negativ.&lt;/li&gt;&#10;&lt;li&gt;Y-0 befindet sich auf der &amp;ldquo;linken&amp;rdquo; Seite der Achse. Die Bewegungsrichtung ist positiv.&lt;/li&gt;&#10;&lt;li&gt;Z-0 befindet sich auf der &amp;ldquo;oberen&amp;rdquo; Seite der Achse. Die Bewegungsrichtung ist negativ.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Für meine Maschine (sie ist breiter als lang), ihre Zugänglichkeit und den Raum, den ich zur Verfügung habe, habe ich die Achsen umgelegt. Bei mir ist die X-Achse die lange Achse des Portals und die Y-Achse befindet sich parallel zur Linearschine, die das Portal hält. Vom Arbeitstisch aus betrachtet ist der Maschinen-Nullpunkt bei mir also &amp;ldquo;hinten links oben&amp;rdquo;. Also bewegt sich Z in negative Richtung, Y ebenfalls, nur X geht zu positiven Werten.&lt;/p&gt;&#10;&lt;p&gt;Verwirrend, nicht wahr? Ist aber kein Problem, denn später arbeitet man kaum mehr mit Maschinenkoordinaten ;)&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-03_kinematics.jpg" alt="Image: machine kinematics and 0-point" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="bewegungsrichtungen"&gt;Bewegungsrichtungen&lt;/h3&gt;&#10;&lt;p&gt;Für die Konfiguration habe ich meine CNC-Software &lt;a href="https://edingcnc.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Link zu EdingCNC&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; mit der Maschine verbunden. An ihr erkläre ich das Vorgehen, was bei anderen Steuerungsprogrammen im Kern aber ähnlich funktioniert. In de &lt;em&gt;Setup&lt;/em&gt;-Ansicht gebe ich die Anzahl Schritte ein, die vom Motor zu gehen sind, bis sich die Achse um einen Millimeter bewegt: &lt;code&gt;16 Mikroschritte/Schritt&lt;/code&gt; multipliziert mit &lt;code&gt;200 Schritten/Motorumdrehung&lt;/code&gt; geteilt durch &lt;code&gt;10mm/spindelumdrehung&lt;/code&gt; = &lt;code&gt;320 Mikroschritte/mm&lt;/code&gt;. Für die Z-Achse wird der Wert durch die bei mir engere Gewindespindel verdoppelt.&lt;/p&gt;&#10;&lt;p&gt;Die 16 Mikroschritte/Schritt ist der Standardwert, welcher von meinem Maschinenhersteller vorgeschlagen wurde. Es gibt &lt;a href="https://www.faulhaber.com/en/support/technical-support/motors/tutorials/stepper-motor-tutorial-microstepping-myths-and-realities/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;ein paar gute Argumente&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und eine tiefgehende Analyse &lt;a href="https://www.edn.com/why-microstepping-in-stepper-motors-isnt-as-good-as-you-think/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;hier&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; darüber, dass man Microstepping so wenig wie möglich verwenden sollte. Da ich weder eine Genauigkeit von 1µm erreichen muss, noch die Schrittmotoren bei meiner Anwendung sehr hohe Drehmomente bewältigen müssen, sollte das aber kein Thema sein.&lt;/p&gt;&#10;&lt;p&gt;Ohne vorher eine Referenzfahrt durchzuführen (die sowieso nicht funktionieren würde), habe ich die Maschine per Pfeiltasten vorsichtig verfahren, um zu sehen, welche Taste welche Achse in welche Richtung bewegt. Da mein Setup vom &amp;ldquo;Normal&amp;rdquo; abweicht, können die von mir gewählten Werte individuell ausfallen.&#10;Wenn eine Achse nicht mit der Pfeiltaste übereinstimmt (wenn ich z.B. &amp;ldquo;rechts&amp;rdquo; drücke und erwarte, dass sich meine X-Achse nach rechts bewegt, aber die Z-Achse tatsächlich nach unten geht), so konnte ich die Motoranschlüsse am Schaltkasten vertauschen, bis die Tasten mit den erwarteten Achsen übereinstimmten.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-03_setup_kinematics.jpg" alt="Image: machine kinematics EdingCNC setup" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Wenn die Tasten die Maschine nun in die falsche Richtung bewegen - ich drücke z.B: &amp;ldquo;Bild abwärts) aber die Z-Achse bewegt sich nach oben, würde ich das Vorzeichen der Motorschrittauflösung ändern - Nicht besonders intuitiv, was? Ich habe auch ein paar Foreneinträge durchsuchen müssen, bevor ich das raus hatte.&lt;/p&gt;&#10;&lt;p&gt;Dies kam nun dabei heraus:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;X-axis -320.000&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Y-axis 320.000&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Z-axis -640.000&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="grenzen-der-maschinenbewegung"&gt;Grenzen der Maschinenbewegung&lt;/h3&gt;&#10;&lt;p&gt;Meine Maschine hat einen nutzbaren mechanischen Verfahrweg von &lt;code&gt;0 / 732mm&lt;/code&gt; in X-Richtung, &lt;code&gt;-654mm / 0&lt;/code&gt; in Y-Richtung, und &lt;code&gt;-134mm / 0&lt;/code&gt; in Z-Richtung. Diese Werte habe ich in die Maske für &lt;code&gt;negative limit&lt;/code&gt; / &lt;code&gt;positive limit&lt;/code&gt; eingegeben und die Vorzeichen auf Basis der Bewegungsrichtung relativ zum Maschinennullpunkt eingestellt.&lt;/p&gt;&#10;&lt;h3 id="referenzschalter-triggerpegel"&gt;Referenzschalter: Triggerpegel&lt;/h3&gt;&#10;&lt;p&gt;Die Achsen der meisten Maschinen haben einen &lt;a href="https://cncphilosophy.com/cnc-offsets-and-machine-reference-explained/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Referenzschalter&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Diese Schalter werden verwendet, um die Maschine zu &amp;ldquo;referenzieren&amp;rdquo;. Die Referenzfahrt ist bei jedem Start der Maschine erforderlich, da die Software intern nur die XYZ-Position durch Akkumulation der Motorschritte ermittelt. Sobald das System neu gestartet wird, gehen alle diese Werte auf 0 zurück, so dass die Maschine ihre Position vergisst.&lt;/p&gt;&#10;&lt;p&gt;Ich habe meine Schalter so angeschlossen, dass sie &amp;ldquo;normalerweise geschlossen&amp;rdquo; sind: Ihre Kontakte sind stromführend, wenn sie nicht ausgelöst werden. Auf diese Weise kann eine fehlerhafte, offene Verbindung sofort erkannt werden, da die Maschine in diesem Fall (erfolglos) mit einer ungewöhnlich niedrigen Geschwindigkeit anfahren würde: &lt;code&gt;homeVelocitySlow&lt;/code&gt; ist in meiner Konfiguration nur 1/10 der normalen Fahrgeschwindigkeit für das Anfahren der Nullpunkte. Trotzdem müssen wir sicherstellen, dass der Referenzfahrt-Kontakt richtig interpretiert wird. Dies kann im Abschnitt &amp;ldquo;Homing and E-Stop&amp;rdquo; des &amp;ldquo;Setup&amp;rdquo;-Bildschirms eingestellt werden. Ich gebe also eine &amp;ldquo;1&amp;rdquo; für &amp;ldquo;Normalerweise geschlossen&amp;rdquo; ein.&lt;/p&gt;&#10;&lt;h3 id="referenzschalter-position"&gt;Referenzschalter: Position&lt;/h3&gt;&#10;&lt;p&gt;Einige CNC-Maschinen bieten eine Positioniersteuerung mit geschlossenem Regelkreis - sie benötigen keine Referenzschalter, da ihre Motoren mit Positionsgebern ausgestattet sind, die die absolute Position jeder Achse zurückmelden. Diese Maschinen verlieren keine Schritte und sind in der Lage, Positionierungsfehler zu erkennen, die z. B. durch mechanische Überlastung verursacht werden. Man nennt solche Motoren meist &lt;em&gt;Servos&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Mit Hilfe des Referenzschalters kann eine Maschine ohne geregelte Positionierung - wie meine - ihre Schrittzahl an einer bekannten Position zurücksetzen, so dass die Achsbewegung wiederholbar wird. Allerdings muss die Position des Referenzschalters bekannt sein, um die Schrittzählung korrekt den Achsenpositionen zuzuordnen, und deshalb müssen wir die Positionen der Referenzschalter an den Achsen eingeben. Ich habe mit Vorsicht beobachtet, wie weit ich die Maschine nach dem Auslösen des Referenzschalters weiter verfahren kann, bevor ich an das mechanische Ende einer Achse stoße (Menü &amp;ldquo;Jog&amp;rdquo;). Dies sind die Werte, die ich schließlich für meine Maschine eingegeben habe:&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Steps/AppUnit&lt;/em&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;X-axis: 10mm&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Y-axis: -4mm&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Z-axis: -16mm&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="not-aus"&gt;Not-Aus&lt;/h3&gt;&#10;&lt;p&gt;Der Not-Aus ist ein weiterer Eingang für die CNC-Software und -Hardware. Durch Drücken dieses Schalters wird die Achsenbewegung gestoppt und im Zuge dessen sowohl Spindel, Kühlschmierstoffzufuhr als auch Hilfsstromkreise deaktiviert. Er ist auch &amp;ldquo;normal geschlossen&amp;rdquo; verdrahtet, um Verbindungsprobleme zu erkennen, und kann wie ein Referenzschalter konfiguriert werden.&lt;/p&gt;&#10;&lt;p&gt;Wenn der Not-Aus-Schalter aktiviert wurde, müssen die Treiber der Maschine zurückgesetzt und die Maschine erneut referenziert werden, bevor sie nach dem Loslassen der Taste wieder normal arbeiten kann.&lt;/p&gt;&#10;&lt;p&gt;Das Not-Aus Signal wird nicht nur an die Software gemeldet, sondern sorgt auf der Steuerplatine selbst für das Abfallen der entsprechenden Relais, sodass die Maschine in den sicheren Zustand gebracht werden kann.&lt;/p&gt;&#10;&lt;h2 id="bewegungsparameter-der-achsen"&gt;Bewegungsparameter der Achsen&lt;/h2&gt;&#10;&lt;p&gt;Die CNC-Software muss wissen, wie sie die Bewegung der Maschinenachsen richtig handhaben muss: Eine schwerere und steifere Maschine mit starken Schrittmotoren wird natürlich schneller verfahren und starke Beschleunigungen sowie ruckartige Bewegungen besser bewältigen können als weichere, leichtere Maschinen. Aus diesem Grund sollten die Bewegungsparameter Ihrer Maschine sorgfältig ermittelt und in der Software hinterlegt werden.&lt;/p&gt;&#10;&lt;h3 id="achsgeschwindigkeit"&gt;Achsgeschwindigkeit&lt;/h3&gt;&#10;&lt;p&gt;&amp;ldquo;Warum sollte Zerspanobert schnell sein?&amp;rdquo; habe ich mich gefragt. Mir fielen nicht allzu viele gute Antworten ein, da die normalen Vorschubgeschwindigkeiten mit den geplanten Schaftfräsern weit unter den Möglichkeiten der Maschine liegen werden. Aber egal, hier sind einige Argumente, eine Maschine schnell verfahren zu lassen:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;die Arbeitszeiten sollten nicht zu lang werden&lt;/li&gt;&#10;&lt;li&gt;lange Verfahrwege, die mit einer großen Maschine zurückzulegen sind&lt;/li&gt;&#10;&lt;li&gt;beim Schneiden weicher Materialien mit geringer Dichte können die Vorschübe viel höher sein&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Wollen Sie noch immer eine schnelle Maschine? Einige Berechnungen helfen uns hier weiter.&lt;/p&gt;&#10;&lt;h4 id="bestimmung-der-fähigkeiten-der-bewegungselektronik"&gt;Bestimmung der Fähigkeiten der Bewegungselektronik&lt;/h4&gt;&#10;&lt;p&gt;Die maximale Geschwindigkeit wird durch mehrere Faktoren begrenzt:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Parameter&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Symbol&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Wert&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Einheit&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;maximale Antriebsfrequenz&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;fD&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;125&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;kHz&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Mikroschritt-Einstellung des Antriebs&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;N&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;16&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;max. Software-Frequenz&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;fS&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;125&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;kHz&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schrittzahl/Umdrehung&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;nS&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;200&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schrittmotor max. Drehzahl&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;nSm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3300&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1/min&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Kugelgewindebreite&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;P&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,01&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;m&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Kugelumlaufspindel max. Drehzahl&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;nLm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1700&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1/min&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Nehmen wir ein Beispiel für einen 125-kHz-Treiber und nehmen wir an, dass die Software diese Schrittfrequenz unterstützt. Nehmen wir weiter an, dass die Motoren 200 Schritte/Umdrehung haben und der Treiber auf 16 Mikroschritte/Schritt konfiguriert ist.&lt;/p&gt;&#10;&lt;p&gt;Damit erreicht man eine Maximaldrehzahl der Kugelumlaufspindel von \(\frac{125000}{3200} = 39\) Umdrehungen pro Sekunde oder &lt;code&gt;2340 U/min&lt;/code&gt;, was für einen Schrittmotor echt zügig ist. Auf einer Kugelumlaufspindel mit &lt;code&gt;10mm&lt;/code&gt; Gewindeweite führt das zu einer theoretischen Verfahrgeschwindigkeit von &lt;code&gt;23.4m/min&lt;/code&gt;, was auf einer kleinen Maschine wie meiner unglaublich schnell aussieht.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.schallbert.de/portal-milling-setup/#update"&gt;Achtung! Korrekturen / Updates weiter unten&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h4 id="grenzen-der-mechanik"&gt;Grenzen der Mechanik&lt;/h4&gt;&#10;&lt;p&gt;Gut, dann schauen wir mal, ob die Kugelumlaufspindeln dieses Tempo mitgehen. Die auf meiner Maschine verbaute Serie &lt;code&gt;KGS16&lt;/code&gt; hat eine ungestützte Strecke von &lt;code&gt;~850mm&lt;/code&gt; zwischen den Lagern auf der langen Achse. Aus der &lt;a href="https://www.neff-gewindetriebe.de/fileadmin/PDF-Datenblaetter/berechnungsgrundlagen/kugelgewindetriebe/berechnungsgrundlagen_kgt.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Online-Dokumenation des Herstellers&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; entnehme ich hierbei eine maximale Drehzahl von &lt;code&gt;1700U/min&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Damit bleiben 70% von der anfänglichen Maximalgeschwindigkeit übrig: &lt;code&gt;17m/min&lt;/code&gt;. Immer noch echt flink.&lt;/p&gt;&#10;&lt;p&gt;Nun wissen wir aber, dass das Moment der Schrittmotore bei steigender Geschwindigkeit abnimmt. Wenden wir unseren Blick mal dorthin:&lt;/p&gt;&#10;&lt;h4 id="genügend-motormoment"&gt;Genügend Motormoment?&lt;/h4&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-03_torque_curve.jpg" alt="ESM.6088.42 Schrittmotor: Drehmomentverlauf" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Datasheet: Der Drehmomentverlauf meines Schrittmotors (Bild bereitgestellt durch Sorotec GmbH)&lt;/span&gt;&lt;a href="https://www.sorotec.de/webshop/Datenblaetter/Schrittmotoren/ESM.6088.42/ESM.6088.42_V1.1.pdf" class="attr-link" aria-label="Attribution 1"&gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Der blaue Pfeil markiert meinen Zielvorschub zum Schruppen in Holz. Hier habe ich ordentlich Drehmoment zur Verfügung um auch tiefe Nuten in einem Durchgang fertigen zu können. Selbst bei schnellen Schlichtdurchläufen oder sehr weichen Materialien (grüner Pfeil) habe ich noch fast 50% des maximalen Drehmoments zur Verfügung. Um noch genügend Luft bis zur kritischen Drehzahl der Kugelumlaufspindel zu haben, wähle ich eine maximale Geschwindigkeit für den Eilgang (&lt;code&gt;G0&lt;/code&gt;) von &lt;code&gt;12m/min = 200mm/sek&lt;/code&gt;. An diesem Punkt sind von den ursprünglich &lt;code&gt;3Nm&lt;/code&gt; nur noch &lt;code&gt;0.7Nm&lt;/code&gt; verfügbar.&lt;/p&gt;&#10;&lt;p&gt;Mit dieser Konfiguration habe ich eine recht schnelle Maschine bei genügend Luft zu kritischen Werten.&lt;/p&gt;&#10;&lt;h3 id="acceleration"&gt;Beschleunigungen&lt;/h3&gt;&#10;&lt;p&gt;Stellen Sie sich vor, Sie möchten in einem Ihrer Programme einen 90°-Winkel bei einem Vorschub von &lt;code&gt;12m/min&lt;/code&gt; fahren. Ähnlich wie bei dem Versuch, eine sehr enge Kurve auf dem Fahrrad zu nehmen, ist dies nicht bei hoher Geschwindigkeit möglich. Man muss erst auf eine Geschwindigkeit herunterbremsen, bei der die Kurve gefahrlos genommen werden kann und danach wieder hochbeschleunigen.&lt;/p&gt;&#10;&lt;p&gt;Wenn wir bei der Maschine nicht bereit sind zu warten, bis eine Achse quasi zum Stillstand gekommen ist bevor die andere losfährt, schneiden wir die Ecke rund. Wenn wir jedoch bremsen, ist unsere Bahngeschwindigkeit nicht mehr konstant - ein Dilemma. Um hier einen möglichst guten Kompromiss zu erreichen, ist die Ermittlung einer passenden Maschinenbeschleunigung wichtig.&lt;/p&gt;&#10;&lt;p&gt;Auf dem Fahrrad vermeidet man beim Bremsen bewusst das Blockieren eines Rades, um die Stabilität nicht zu gefährden. Auf der CNC gilt wieder um Ähnliches: Hinreichend hohe Beschleunigungen erreichen, ohne Schrittverlust, unnötige Schwingungen, Verklemmung / Blockade einer Achse, starke Rucke oder Torsion zu riskieren. Zudem möchten wir Maschinenverschleiß minimieren.&lt;/p&gt;&#10;&lt;p&gt;Klingt schwierig? Ist machbar, benötigt aber etwas Mathematik und Gefühl.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-03-03_cnc_cornering.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Kurven zeichnen: `vmax = 200mm/s`, `a=400mm/s^2`&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Das Video oben illustriert den Sachverhalt: Bei Kanten und engen Kurven bremst die Maschine merklich ab um den Richtungswechsel hinzubekommen.&lt;/p&gt;&#10;&lt;h4 id="motivation"&gt;Motivation&lt;/h4&gt;&#10;&lt;p&gt;Warum möchte ich, dass Zerspanobert hohe Beschleunigungswerte unterstützt? Ein paar meiner Antworten: Meine Projekte haben oft eine Menge kurzer Liniensegmente und 2.5D-Arbeiten. Die Formen sind komplex und die G-Code Dateien zumeist recht lang. Daher verwende ich auch den &lt;a href="https://www.mmsonline.com/articles/a-closer-look-at-look-ahead" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Look-Ahead Feed&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; &lt;code&gt;G64&lt;/code&gt; mit &lt;code&gt;0.1mm&lt;/code&gt; erlaubter Abweichung vom Pfad.&lt;/p&gt;&#10;&lt;p&gt;Außerdem möchte ich möglichst lange mit konstanter Geschwindigkeit durch das Material fahren. So vermeide ich Stellen, an denen das Material heißer wird als nötig. Nicht zuletzt will ich natürlich nicht unnötig viel Zeit an der Maschine verbringen - Die Maschine arbeitet mit höheren Beschleunigungswerten einfach deutlich schneller; dies wirkt sich bei meinen Projekten sogar stärker aus als ein noch flinkerer Eilgang.&lt;/p&gt;&#10;&lt;h4 id="bestimmung-der-maximalen-achsbeschleunigung"&gt;Bestimmung der maximalen Achsbeschleunigung&lt;/h4&gt;&#10;&lt;p&gt;Dann ermitteln wir mal den maximal möglichen Wert für die Maschinenbeschleunigung.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Newton%27s_laws_of_motion" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Newtons zweites Gesetz&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; (axiale Bewegung): \(F_{ax} = m_pa\)&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Torque" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Drehmoment&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; an der Achse: \(M_a = \frac{a J}{P}\)&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Moment_of_inertia" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Trägheitsmoment&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; der Achse (Zylinder): \(J = m \frac{a}{2}r^2\)&lt;/li&gt;&#10;&lt;li&gt;Benötigtes &lt;a href="https://tech.thk.com/de/products/pdf/de_b17_009.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Motormoment&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; für die Linearbewegung: \(M_d =\frac{F_{ax} P}{2 \pi \eta} + M_a\)&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Wenn ich diese Formeln kombiniere, erhalte ich für die Beschleunigung:&lt;/p&gt;&#10;$$a = \frac{M_d}{\frac{m_p P}{2 \pi \eta} + \frac{J}{P}}$$&lt;p&gt;In Worten: Die höchste Beschleunigung, die die Maschine theoretisch erreichen kann, ist das verfügbare Motormoment \(M_d\) geteilt durch die Masse des Portals \(m_p\), gewichtet mit spezifischen Parametern der Kugelumlaufspindel wie Gewindespreizung \(P\) und Wirkungsgrad des bewegten Systems \(\eta\) zuzüglich des Trägheitsmoment \(J\) der rotierenden Komponenten, wiederung gewichtet durch die Gewindespreizung.&lt;/p&gt;&#10;&lt;h4 id="konkrete-richtwerte"&gt;Konkrete Richtwerte&lt;/h4&gt;&#10;&lt;p&gt;Setzen wir mal ein paar Werte ein - meine BasicLine 0607:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Parameter&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Symbol&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Wert&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Einheit&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Stepper torque @ vmax&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Md&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.5&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Nm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Portalmasse&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;mp&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;40&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;kg&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Spreizung Gewindespindel&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;P&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.01&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;m&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Radius Gewindespindel&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;r&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.008&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;m&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Rotierende Masse&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;ma&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;kg&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Wirkungsgrad der Bewegung&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;n&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;75&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Wo ich keine exakten Werte zur Verfügung hatte, habe ich worst-case Annahmen getroffen. Beim Schrittmotor z.B. meine Maximalgeschwindigkeit bei geringster Versorgungsspannung. Auch das Gewicht der rotierenden Masse (Spindel, Kupplung, Rotor und Achse des Motors) habe ich geschätzt sowie den Wirkungsgrad von Kugelumlaufspindel, Lagern und Linearführungen.&lt;/p&gt;&#10;&lt;p&gt;Dies ist mein Ergebnis (Trommelwirbel):&lt;/p&gt;&#10;$$a = 5.0m/s^2$$&lt;h4 id="was-dieser-wert-bedeutet"&gt;Was dieser Wert bedeutet&lt;/h4&gt;&#10;&lt;p&gt;Wie lange benötigt die Maschine, um auf Höchstgeschwindigkeit zu beschleunigen?&#10;Nehmen wir unser Physikwissen aus der Sekundarstufe: &lt;a href="https://en.wikipedia.org/wiki/Acceleration" target="_blank" rel="noopener noreferrer" class="external-link"&gt;gleichmäßig beschleunigte Bewegung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&#10;$$v(t) = at \Rightarrow t = \frac{v_{max}}{a} = 40ms$$&lt;p&gt;Wie groß ist der Weg, den wir dabei zurücklegen (&lt;/p&gt;&#10;$$s_0 = 0, v_0 = 0$$&lt;p&gt;)?&lt;/p&gt;&#10;$$s(t) = \frac{a}{2}t^2 + v_0t + s0 \Rightarrow s(t=40ms) = 4mm$$&lt;p&gt;Welche Axialkräfte wirken dabei auf das Portal ein? &lt;/p&gt;&#10;$$F_{ax} = m_pa = 200N$$&lt;p&gt;Die von mir hier errechnete Beschleunigung ist mehr als eine Größenordnung über der Empfehlung des Herstellers von &lt;code&gt;300mm/s^2&lt;/code&gt;. Ich habe mich da deutlich verschätzt.&lt;/p&gt;&#10;&lt;p&gt;🤔 &lt;em&gt;Ich muss etwas übersehen haben.&lt;/em&gt;&lt;/p&gt;&#10;&lt;h4 id="zusammenfassung-erfahrung-zählt"&gt;Zusammenfassung: Erfahrung zählt!&lt;/h4&gt;&#10;&lt;p&gt;Die Formeln oben berücksichtigen weder die Steifheit der Maschine noch Ruck, Torsion oder auftretende Schwingungen unter dynamischer Last. Was ein &amp;ldquo;Ruck&amp;rdquo; im CNC-Kontext darstellt? Dieses kurze &lt;a href="https://www.youtube.com/watch?v=9Z9Zws9K5kc" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Youtube-Video&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; klärt auf. Meine Steuersoftware jedoch verfügt nicht über eine Ruckbegrenzung.&lt;/p&gt;&#10;&lt;p&gt;Obwohl ich viel darüber gelernt habe, wie man sich mit Hilfe der Mathematik eine Einordnung der Parameter ermöglicht, läuft alles wieder auf die Erfahrung hinaus:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Wie schwer und stabil ist die Maschine?&lt;/li&gt;&#10;&lt;li&gt;Mit welcher Art von Material arbeite ich: Ist es nachgiebig oder steif?&lt;/li&gt;&#10;&lt;li&gt;Habe ich komplexe oder möglicherweise langwierige Fräsaufgaben zu erledigen?&lt;/li&gt;&#10;&lt;li&gt;Arbeitet meine CNC in einer Produktionsumgebung, in der die Maschinenzeit wichtig ist?&lt;/li&gt;&#10;&lt;li&gt;Wie klingt und fühlt sich die CNC bei hohen Beschleunigungswerten an?&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Ich habe einen Wert von 800 mm/s^2 als Beschleunigung für die X- und Y-Achse eingegeben. Das schien ohne Probleme zu funktionieren. Immerhin bleibt er um den Faktor 6 unter der berechneten Fähigkeit des Bewegungssystems.&lt;/p&gt;&#10;&lt;h2 id="ausprobieren"&gt;Ausprobieren&lt;/h2&gt;&#10;&lt;p&gt;Ich weiß, das war eine lange Lektüre und viel mehr eine fundierte Vermutung als eine strenge Berechnung 😅. Trotzdem sind wir jetzt bereit, die Maschine das erste Mal in Betrieb zu nehmen.&#10;Probieren Sie es aus. Gehen Sie danach im manuellen Betrieb auf allen Achsen auf auf den Maschinennullpunkt. Hält sie an, bevor sie den mechanischen Endstopp erreicht?&lt;/p&gt;&#10;&lt;p&gt;Verwenden Sie dann den kontinuierlichen Tippbetrieb, um sich dem anderen Ende aller Achsen anzunähern. Bewegen Sie vorsichtig die letzten Millimeter. Sind alle Messungen und Einstellungen korrekt?&#10;Wenn ja, versuchen Sie es mit der 100%-Geschwindigkeitstaste (&lt;code&gt;Shift&lt;/code&gt; - auf meiner Tastatur). Bewegt sich die CNC schnell und stoppt automatisch am Achsenende ohne Kollision?&lt;/p&gt;&#10;&lt;p&gt;Herzlichen Glückwunsch!&lt;/p&gt;&#10;&lt;h2 id="rechner-für-maximale-bewegungseinstellungen"&gt;Rechner für maximale Bewegungseinstellungen&lt;/h2&gt;&#10;&lt;p&gt;Sie haben tapfer den ganzen Artikel gelesen. Als Belohnung habe ich ein kleines Excel-Tool vorbereitet: &lt;a href="https://blog.schallbert.de/assets/docs/cnc_kinematics_helper.xlsx"&gt;CNC kinematics helper&lt;/a&gt;, das Sie herunterladen und verwenden können, um Schätzungen für Ihre Maschine zu berechnen. Natürlich kann ich keine Verantwortung für die Ergebnisse übernehmen, die Sie bei der Verwendung dieses Tools erhalten. Selbiges gilt für Schäden, die falsche Werte verursachen können.&lt;/p&gt;&#10;&lt;h2 id="update"&gt;Update&lt;/h2&gt;&#10;&lt;p&gt;Ich habe nochmal mit den maximalen Geschwindigkeits- und Beschleunigungswerten für meine Maschine herumgespielt.&#10;&lt;code&gt;300mm/s&lt;/code&gt; war zu schnell und ich bekam Software-Limit Verletzungsfehler. Bei &lt;code&gt;240mm/s&lt;/code&gt; hat meine Maschine stark vibriert. Also habe ich die Höchstgeschwindigkeit bei &lt;code&gt;220mm/s&lt;/code&gt; belassen, was in Ordnung zu sein scheint.&#10;Außerdem habe ich die Beschleunigung auf &lt;code&gt;1200mm/s^2&lt;/code&gt; erhöhen können. Das einzige Problem mit diesem neuen Wert ist, dass die Maschine bei kurzen Bewegungen ein bisschen schwingt. Wenn die Beschleunigung abgeschlossen ist, wird alles wieder ruhiger. Schauen wir mal, ob sich das auf die Bearbeitungsqualität auswirkt.&lt;/p&gt;&#10;&lt;h2 id="update-2"&gt;Update 2&lt;/h2&gt;&#10;&lt;p&gt;Die obige Berechnung ist zwar mathematisch nicht falsch, aber sie ist &lt;em&gt;irreführend&lt;/em&gt;. Sie suggeriert, dass jede Achse mit dieser Geschwindigkeit laufen könnte, was einfach nicht der Fall ist. Ich habe mir vom &lt;a href="https://edingcnc.com/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Anbieter der Steuerungssoftware für meine Maschine&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; bestätigen lassen, dass die 125kHz die maximale Befehlsfrequenz des &lt;strong&gt;Systems&lt;/strong&gt; sind. Wenn ich versuche, höhere Geschwindigkeiten zu fahren, erhalte ich die Fehlermeldung &amp;ldquo;Velocity was higher than max!&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Deshalb sollte ein so hoher Geschwindigkeitswert &lt;em&gt;nicht&lt;/em&gt; verwendet werden, wenn die Maschine mit mehreren Achsen gleichzeitig im Eilgang läuft. Ich habe einen Folgeartikel verfasst und &lt;a href="https://blog.schallbert.de/projects/one-year-zerspanobert/#velocityerror"&gt;hier&lt;/a&gt; zur Information verlinkt. Dort verwende ich Werte, die stabil in jeder Situation funktionieren, auch wenn alle Achsen mit voller Geschwindigkeit gleichzeitig betrieben werden.&lt;/p&gt;&#10;&lt;h2 id="noch-immer-weiterlesen"&gt;Noch immer weiterlesen?&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/edingcnc-config/"&gt;Einrichtung der Maschinensoftware&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>CNC Teil3 - Aufbau</title><link>https://blog.schallbert.de/portal-milling-build/</link><pubDate>Tue, 01 Mar 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/portal-milling-build/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-machine_complete-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Completed build of Zerspanobert - Sorotec Basicline 0607"&#10; title="CNC Teil3 - Aufbau" /&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/aAF2gaBewonh2uvu6mspze" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;&amp;amp;quot;Zerspanobert&amp;amp;quot; CNC assembly&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Dies ist der dritte Teil meiner Artikelserie über Portalfräsmaschinen. Er konzentriert sich auf den mechanischen/mechatronischen Aufbau meiner Maschine.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/portal-milling/"&gt;&lt;em&gt;Teil 1&lt;/em&gt;: Gedanken zu Portalfräsmaschinen und ihre Auslegung für den (ambitionierten) Hobbybereich&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/cnc-electronics/"&gt;&lt;em&gt;Teil 2&lt;/em&gt;: CNC Elektrik / Elektronik&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/portal-milling-setup/"&gt;&lt;em&gt;Teil 4:&lt;/em&gt; Einrichten der Maschine&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/edingcnc-config/"&gt;&lt;em&gt;Teil 4.1:&lt;/em&gt; Konfiguration der Software&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/macros-for-cnc/"&gt;&lt;em&gt;Teil 5:&lt;/em&gt; Makros für die CNC&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="marke-sorotec-produkt-basicline-0607"&gt;Marke: Sorotec, Produkt: Basicline 0607&lt;/h2&gt;&#10;&lt;p&gt;Meine Portalfräsmaschine wurde als Komplettbausatz geliefert. Nach einer groben Prüfung auf Vollständigkeit konnte ich voller Vorfreude auf die fertige Maschine mit dem Zusammenbau starten.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-allparts.jpg" alt="Image: parts included in the assembly kit" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="eckdaten-der-maschine"&gt;Eckdaten der Maschine&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Stellfläche etwa 1m x 1m x 0.8m&lt;/li&gt;&#10;&lt;li&gt;Arbeitsbereich ~ 654mm x 732mm x 134mm&lt;/li&gt;&#10;&lt;li&gt;Gewicht ohne Spindel, Elektrosatz, Zubehör: 52kg (laut Hersteller)&lt;/li&gt;&#10;&lt;li&gt;Kugelumlauftriebe und Linearführungen auf allen Achsen&lt;/li&gt;&#10;&lt;li&gt;Schrittmotoren: &lt;code&gt;3Nm, 4.2A @48V&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Empfohlene maximale Bewegungsgeschwindigkeit: &lt;code&gt;130mm/s | 7.8m/min, 400mm/s²&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="zusammenbau"&gt;Zusammenbau&lt;/h2&gt;&#10;&lt;p&gt;Der Aufbau der Mechanik und Mechatronik dauerte etwa 16h inkl. Verdrahtung der Schrittmotoren, Stecker und aller Leitungen. Nicht berücksichtigt ist die Maschinensteuerung, worüber ich &lt;a href="https://blog.schallbert.de/cnc-electronics/"&gt;hier&lt;/a&gt; schreibe.&lt;/p&gt;&#10;&lt;p&gt;Die nun folgenden Abschnitte beschreiben je eine Phase des Zusammenbaus. Ich verschweige auch nicht die Schwierigkeiten, die ich dabei hatte.&lt;/p&gt;&#10;&lt;h3 id="1-vorbereitung-der-lager-für-die-kugelumlaufspindeln"&gt;1. Vorbereitung der Lager für die Kugelumlaufspindeln&lt;/h3&gt;&#10;&lt;p&gt;Die Kugelumlaufspindel (&lt;a href="https://www.youtube.com/watch?v=W27pJXlO6ko" target="_blank" rel="noopener noreferrer" class="external-link"&gt;youtube link&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) ist eine mechanische Vorrichtung, die eine Drehbewegung in lineare Bewegung umwandelt. Dabei wird die mit einem Gewinde versehene Spindel gedreht und dadurch ein auf der Spindel befindlicher Schlitten verfahren. Der Unterschied zur auf demselben Konzept basierenden Trapezgewindespindel ist, dass die Spindelmutter über ein umlaufendes System von Kugeln verfügt, welche Reibung und Spiel zwischen Mutter und Spindel reduzieren. Bei meiner CNC-Maschine werden sie eingesetzt, um die Achsen mit Hilfe eines Schrittmotors zu verfahren.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-ballscrews.jpg" alt="Image: ballscrews with bearings" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die Kugelumlaufspindeln besitzen zwei Lager. Ein Festlager auf der Schrittmotorseite (der schwarze Block auf den Bildern) und ein Loslager, das die andere Seite der Spindel an ihrem Platz hält. Die Montage des Loslagers in seine Halterung stellte sich als schwierig heraus. Ich habe keinerlei Erfahrung im Metallbau, also habe ich statt der klugen Lösung (Kugellager abkühlen, Halterung erwärmen) die einfache gewählt (Hämmern). Ergebnis: das Lager saß schief.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-03-01_fitting-bad.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Falsch eingesetztes Loslager&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Glücklicherweise hatte ich eine &lt;code&gt;20mm&lt;/code&gt; Nuss zur Hand die Außendurchmesser des Lagers nahekam. So war ich in der Lage, das Lager erst wieder heraus zu schlagen und dann erneut - sanft und gleichmäßig - in seinem Sitz zu befestigen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-03-01_fitting-ok.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Verhalten nach Korrektur des Lagersitzes&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Ich hoffe mal, dass ich das Lager bei meinen Fehlversuchen nicht zu sehr mit Aluminiumspänen verunreinigt habe.&lt;/p&gt;&#10;&lt;h3 id="2-maschinenrahmen"&gt;2. Maschinenrahmen&lt;/h3&gt;&#10;&lt;p&gt;Der Rahmen meiner Maschine besteht aus Aluminium-Stranggussprofilen, welche mit Winkeln und Nutensteinen miteinander verbunden werden. Massive Aluminiumplatten and Vorder- und Rückseite der Maschine versteifen die Konstruktion zusätzlich. Die Linearführungen werden an der Seite des Rahmens befestigt. Sie halten später das Portal und stellen die Bewegung der X-Achse (Definition des Herstellers) sicher.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-frame.jpg" alt="image: frame of Basicline 0607" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Ich habe ziemlich viel Zeit darauf verwenden müssen, den Rahmen ordentlich auszurichten. Als endlich alle Winkel 90° betrugen, konnte ich einen kleinen Luftspalt an der rechten Rahmenwange lassen, sodass die Portalbreite die Rahmenbreite vorgeben kann und ich nichts verspanne. Als ich die Schrauben jedoch anzog, schien der Rahmen sich stets aus dem rechten Winkel zu verabschieden. Als ich das Spiel ein paarmal erfolglos wiederholt hatte, begann ich, die Schrauben in mehreren Stufen gleichmäßig in &lt;code&gt;3Nm&lt;/code&gt;-Schritten anzuziehen.&lt;/p&gt;&#10;&lt;p&gt;Auf diese Weise konnte ich endlich meine &lt;code&gt;24Nm&lt;/code&gt; bei guter Rechtwinkligkeit 😅 erreichen&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-xaxis.jpg" alt="Image: X-axis fully mounted, view from the rear" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die Montage der Front- und Rückplatte, Kugelumlaufspindel der Y-Achse sowie des Referenzschalters stellten sich als angenehm schnell und einfach heraus. Das obige Bild zeigt die fertig montierte X-Achse. Linearführungen (ebenfalls mit Kugelumlauf), die später das Portal tragen werden, sind bereits montiert. So sehen diese Dinger von innen aus:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-03-01_trolley.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Linearführungen mit Kugelumlauf&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="3-portal"&gt;3. Portal&lt;/h3&gt;&#10;&lt;p&gt;Zuerst werden die Portalwangen an den Linearführungen der Y-Achse befestigt. Dabei vereinfachen Metallstifte, die in bereits vorhandene Bohrungen gesteckt werden, die präzise Positionierung. Anschließend werden die Führungsschienen für die Y-Achse am Querbalken des Portals befestigt und dieser dann an der Portalwange montiert. Winkel an beiden Seiten der Portalwange versteifen die Konstruktion. Um die Montage zu erleichtern verwendete ich zwischenzeitlich Schraubzwingen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-portal.jpg" alt="Image: Gantry mounted, secured with clamps" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Diese Maschine arbeitet mit einem Schrittmotor für die Y-Achse, dessen Kugelumlaufspindel mittig am Portalunterzug befestigt wird. Dieser ist im Video unten gut zu erkennen. Da die Maschine langsam an Gewicht zulegte, habe ich mir einen kleine Holzkonstruktion gebaut, um die für die Montage schräg auf der Seite liegende Maschine zu sichern.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-03-01_portal-move.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Das Portal lässt sich ohne Kugelumlaufspindel leicht von Hand bewegen.&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Nun konnte ich die Schrauben am Luftspalt des Rahmens nochmal vorsichtig lösen, das Portal ein paar mal bewegen um mögliche Verspannungen heraus zu bekommen und sie anschließend wieder gleichmäßig anziehen.&lt;/p&gt;&#10;&lt;p&gt;Als Nächstes waren die Kugelgewindespindel und Linearführungen der X-Achse zu montieren. Auf diesen wird - wieder mit Positionierstiften versehen - eine Flanschplatte montiert, die später die Z-Achse hält.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-yaxis.jpg" alt="Image: X-axis with Z-flange before attaching to ball screw" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Zum Schluss waren noch Referenzschalter und Schrittmotorhalterung anzubringen.&lt;/p&gt;&#10;&lt;h3 id="4-z-achse"&gt;4. Z-Achse&lt;/h3&gt;&#10;&lt;p&gt;Die Z-Achse wir als eigene Baugruppe unabhängig vom Rest der Maschine montiert. Auch hier kommen wieder Passstifte zum Einsatz für die Flanschplatte. Bei meiner Maschine hält eine einzelne Linearführung die Z-Achse an ihrer Position.&lt;/p&gt;&#10;&lt;p&gt;Dies ist für meine Anwendungsbereiche mit kleinen Fräsern und recht geringen Vorschüben sicherlich OK, wird bei herausfordernderen Materialien, schnellen Vorschüben oder der Verwendung großer Fräser durch die viel höheren Schnittkräfte bestimmt schnell ein Problem. Was soll&amp;rsquo;s, ich mache doch eh nur in Holz und Kunststoffen&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;Es ist ja immer schön wenn Dinge einfach funktionieren 🤗 - Herrlich, wie die Teile des Bausatzes satt ineinander greifen!&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-03-01_zaxis.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Ich liebe das Geräusch, wenn gut passende Bauteile zusammenkommen.&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Als die Baugruppe soweit fertig war, konnte ich sie an der Flanschplatte des Portals befestigen. Anschließend habe ich die Z-Achse mit Hilfe eines Winkels und einer Messuhr ausgerichtet, sodass sie über den gesamten Fahrweg mit nur ein paar Hundertstel Abweichung senkrecht auf dem Maschinenrahmen steht.&#10;Irgendwann muss man es bei diesem Bausatz aber gut sein lassen, da die Z-Achse mit ordentlichem Druck per Hand am unteren Teil fast 1/10mm gegen die Senkrechte wegbiegen kann.&#10;Es ist halt keine superstabile, entsprechend teure Maschine sondern ein Bausatz für den ambitionierten Hobbybereich.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-zaxis_alignment.jpg" alt="Image: Z-axis alignment with dial gauge" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Das Anbringen der seitlichen Versteifungen auf der Z-Achse stellte sich als viel schwieriger heraus als ich dachte. Die T-Nutensteine sind so klein bei der &lt;code&gt;17mm&lt;/code&gt; Schiene und die Schrauben so kurz, dass da kaum Platz blieb um alles an seinen Platz zu bekommen. Ich musste die Nutensteine mehrfach neu positionieren, um die Konstruktion dann einfädeln zu können. Als das endlich klappte, konnte ich den Referenzschalter montieren.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-zaxis_stiffening.jpg" alt="Image: Z-axis stiffening cheeks, reference switch, Y-axis motor mount" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="5-mechatronik"&gt;5. Mechatronik&lt;/h3&gt;&#10;&lt;p&gt;Nun geht es an die vorher &lt;a href="https://blog.schallbert.de/cnc-electronics/#schrittmotortreiber-im-detail"&gt;vorbereiteten Schrittmotoren&lt;/a&gt; . Dafür werden die Mitnehmer der Klauenkopplungen mit Vaseline eingerieben, sodass sie später keine lauten Knarzgeräusche von sich geben. Dann wird die eine Seite der Kupplung an der Kugelumlaufspindel und die andere am Schrittmotor befestigt. Zum Schluss dann montiert man die Motoren und zieht die Schrauben erst richtig fest, wenn Ausrichtung geprüft und ein leichter Lauf sichergestellt werden kann.&lt;/p&gt;&#10;&lt;p&gt;Auf der X-Achse hatte ich ziemliche Schwierigkeiten, die Madenschraube auf der Motorseite festzuziehen. Daher würde ich es anders als in der Anleitung angegeben direkt wie oben beschrieben mit auseinandergenommener Klauenkupplung montieren und die Motoren erst ganz zum Schluss einpassen.&lt;/p&gt;&#10;&lt;p&gt;Da die Motorzuleitung nicht besonders lang ist, gibt es in Nähe der Motoren kleine Klemmenkästen. Dort werden Motorzuleitung und Signaldrähte für die Referenzschalter aufgelegt. Die andere Seite der Klemmen verbindet eine schleppkettentaugliche Leitung später mit der Steuerung.&#10;Zum Schluss wird noch ein Schutzleiter zu jedem Motor gelegt.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-xaxis_wiring.jpg" alt="Image: Wiring X-axis stepper motor" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Als das für alle drei Achsen erledigt war, machte ich mich an die Schleppketten. Hier ist es vorteilhaft, die Montagerichtung bewusst zu wählen - die Schleppketten lassen sich z.B. zur nachträglichen Verlegung einer zusätzlichen Leitung nämlich nur in eine Richtung öffnen. Wenn deren Mechanismus dann von der Maschine verdeckt wird, kann das zu einem unmöglichen Unterfangen werden.&lt;/p&gt;&#10;&lt;p&gt;Es ist ebenfalls empfehlenswert, die Steuerleitungen länger als berechnet zu lassen und sie erst in einem letzten Schritt zu kürzen, nämlich bevor sie an der Steuerung montiert werden. Die Zuleitung zur Z-Achse kann beispielsweise nach Führen durch alle Schleppketten gern mal in Richtung 5m Länge gehen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-mechatronics.jpg" alt="Image: All wiring done." /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="6-abschmieren"&gt;6. Abschmieren&lt;/h3&gt;&#10;&lt;p&gt;Bonusschritt: Der Wartungsanleitung zum Abschmieren aller Linearführungen und Kugelumlaufspindeln folgen. Herrlich, wenn man nochmal unter die Maschine muss&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;Erwähnte ich bereits, dass ich kein Metallfritze bin? Diese Fettpressen sind echt frustrierend. Am Anfang bekam ich so gar nichts aus ihr raus und dann muss ich eine Luftblase im System gehabt haben, sodass ich nicht mehr wusste, wohin mit dem ganzen überschüssigen Schmierstoff. Immerhin habe ich jetzt einen gut gefetteten Arbeitstisch.&lt;/p&gt;&#10;&lt;p&gt;Und wann ist es eigentlich genug? Reicht &amp;ldquo;zwei mal drücken&amp;rdquo;, um eine Linearführung ausreichend zu schmieren? Kommt das Fett überhaupt in die Schmiernippel? Na ja, sobald es aus der Dichtung der Führungen herausquillt wird es wohl reichen 😜&lt;/p&gt;&#10;&lt;h3 id="7-fehlende-teile"&gt;7. Fehlende Teile&lt;/h3&gt;&#10;&lt;p&gt;Wie oben beschrieben waren alle wichtigen teile der Maschine korrekt und entsprechend der Packliste geliefert worden. Allerdings fehlten ein paar KLeinigkeiten für meine Maschinenkonfiguration:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;18x 18mm M5 Maschinenschrauben mit Innensechskant (Linearführungen X-Achse)&lt;/li&gt;&#10;&lt;li&gt;6x 0.75mm Kabelschuhe (Schutzerde für die Schrittmotoren)&lt;/li&gt;&#10;&lt;li&gt;4x 22mm M4 Maschinenschrauben mit Innensechskant (Montage Schrittmotoren)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Aber ich hatte auch ein paar Teile zu viel&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;1m extra Schleppkette&lt;/li&gt;&#10;&lt;li&gt;4 M5 Unterlegscheiben&lt;/li&gt;&#10;&lt;li&gt;9 M4x10 Senkkopfschrauben mit Innensechskant&lt;/li&gt;&#10;&lt;li&gt;2 Versteifungen z-Achse &amp;ldquo;standard&amp;rdquo;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Mein Vorschlag wäre, nochmal 100 Kabelbinder auf den Bausatz draufzuschlagen, sodass man alle Leitungen schön und ordentlich führen kann.&lt;/p&gt;&#10;&lt;h3 id="8-kritik"&gt;8. Kritik&lt;/h3&gt;&#10;&lt;p&gt;Die Bauanleitung (Version 2.1.5) gefällt mir sehr gut. Sie enthält eine Teileübersicht und jeder Schritt ist sehr detailliert erklärt. Auch der Bauprozess ist gut durchdacht und ich als nicht-professioneller Mechaniker hatte keine größeren Probleme, die Maschine korrekt zusammenzubauen.&lt;/p&gt;&#10;&lt;p&gt;Die bearbeiteten Teile haben eine hohe Qualität und sind frei von Graten oder scharfen Ecken. Alle notwendigen Gewinde wurden im Voraus geschnitten - sogar die für den optionalen Mechatronik-Bausatz, so dass ich keine Werkzeuge kaufen oder mir Wissen darüber aneignen musste, wie und wo ich sie montieren muss.&lt;/p&gt;&#10;&lt;p&gt;Was mir nicht gefiel, war, dass einige Befestigungselemente fehlten, was die Fertigstellung um ein Wochenende verzögerte, und dass die erwartete Anzahl der Befestigungselemente nicht in der Anleitung abgedruckt war, so dass ich erst merkte, dass Teile fehlten, als es bereits zu spät war. Mir ist klar, dass diese Maschine in verschiedenen Größen und Konfigurationen bestellt werden kann, so dass es vielleicht zu viel verlangt ist, eine Bauanleitung für jede einzelne Kombination zu erwarten.&lt;/p&gt;&#10;&lt;p&gt;Einige der größeren Aluminiumteile kamen mit Kratzern an, da die Palette während des Transports irgendwie gebrochen und durch die Verpackung gerissen sein muss. Für mich zeigt das, dass entweder das Logistikunternehmen oder der Hersteller die Maschine nicht 100%ig sicher verpackt oder vielleicht das Gewicht unterschätzt hat.&lt;/p&gt;&#10;&lt;p&gt;Ein letzter Punkt zu diesen Schmiernippeln (bin immer noch kein Metallbauer): Der für die Y-Achse kann nicht mehr gedreht werden, wenn die Mutter der Kugelumlaufspindel am unteren Portalträger montiert ist, weil das Spiel fehlt. Das ist unglücklich, wenn die Schmierung plötzlich in einem anderen Winkel als vorgesehen erfolgen muss. Außerdem bin ich mir ziemlich sicher, dass der untere Schmiernippel der Z-Achse im Handbuch in die falsche Richtung zeigt.&lt;/p&gt;&#10;&lt;p&gt;Ich habe mein Bestes gegeben, um Dinge zu finden, über die ich meckern kann. Ich habe es aber nicht geschafft, mehr als die oben genannten Kleinigkeiten zu finden. Keiner meiner Kritikpunkte schränkt die Funktionalität der Maschine ein, und keiner von ihnen kostete eine beträchtliche Menge an Zeit oder Geld, um ihn zu beheben.&lt;/p&gt;&#10;&lt;p&gt;Bisher sieht alles so aus als hätte ich ein robustes Gerät gekauft, das mir helfen wird, meine Prototyping-Holzarbeiten präziser und schneller als je zuvor zu erledigen.&lt;/p&gt;&#10;&lt;p&gt;Darf ich vorstellen: &lt;strong&gt;Zerspanobert&lt;/strong&gt;!&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-03-01-machine_complete.jpg" alt="Image: Sorotec BasicLine 0607 fully built" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;h3 id="9-dann-nehmen-wir-sie-mal-in-betrieb"&gt;9. Dann nehmen wir sie mal in Betrieb&lt;/h3&gt;&#10;&lt;p&gt;Weiterlesen? Gern! &lt;a href="https://blog.schallbert.de/portal-milling-setup/"&gt;Einrichtung der Maschine&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>CNC Teil2 - Elektrik</title><link>https://blog.schallbert.de/cnc-electronics/</link><pubDate>Fri, 18 Feb 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/cnc-electronics/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-02-18_switchbox-closed-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: switchbox of my CNC machine"&#10; title="CNC Teil2 - Elektrik" /&gt;&#10;&lt;h2 id="elektrik-der-portalfräse"&gt;Elektrik der Portalfräse&lt;/h2&gt;&#10;&lt;p&gt;Dies ist der zweite Teil aus der Reihe &lt;a href="https://blog.schallbert.de/portal-milling/"&gt;Portalfräsmaschine&lt;/a&gt;. Hier geht es um Elektrik und Elektronik der Maschine.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-02-18_switchbox.jpg" alt="Image: Switch box, electronics fully assembled, opened" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;h3 id="ac--netzkomponenten"&gt;AC / Netzkomponenten&lt;/h3&gt;&#10;&lt;p&gt;Im Schaltkasten befinden sich Sicherungen für die gesamte Maschinensteuerung und ein zentraler Netzschalter. In meinem Fall enthält er auch netzgekoppelte Relais zur Steuerung von Verbrauchern wie Spindeln, Kühlung und Staubabsaugung. Für diese Stromkreise sind zusätzlich abgesicherte Strompfade und Steckdosen vorhanden.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-02-18_sockets.jpg" alt="Image: Schaltkasten, Steckdosen" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Außerdem verfügt er über ein Netzteil für die Kleinspannungs-Gleichstromschiene. Die Ausgangsleistung ist vorrangig angepasst an die Anforderungen der Schrittmotore. Es versorgt zudem die Steuer-/Breakout-Platine und weitere Spannungswandler, die dann die CNC-Steuerplatine sowie Lüfter und andere Kleinverbraucher mit Energie versorgen.&lt;/p&gt;&#10;&lt;p&gt;Zudem gibt es ein Signalrelais, um einen eventuell vorhandenen Frequenzumrichter zum Betrieb von Hochfrequenzspindeln mit der Steuerung zu koppeln.&lt;/p&gt;&#10;&lt;h3 id="vfd"&gt;Frequenzumrichter&lt;/h3&gt;&#10;&lt;p&gt;Ein FU ist allgemein bekannt als ein Gerät, das die Drehzahl einer 3-Phasen-Wechselstrommaschine steuert. Er wird mit 1- oder 3-phasigem Wechselstrom versorgt und puffert die Energie in einem Gleichstrom-Zwischenkreis, von wo aus sie dann an eine 3-phasige &lt;a href="https://en.wikipedia.org/wiki/H-bridge" target="_blank" rel="noopener noreferrer" class="external-link"&gt;H-Brücke&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; weitergeleitet wird, die daraus Drehstrom mit variabler Frequenz erzeugt.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-02-18_vfd-schematics.jpg" alt="Image: Vereinfachte schematische Darstellung des Leistungspfads eines VFD" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Diese dreiphasige Wechselspannung wird von Transistoren im Schaltbetrieb erzeugt, was die Umwandlungsverluste im Vergleich zum linearen Betrieb enorm verringert. Die Ausgangsspannung ist allerdings zeitlich und betragsmäßig quantifiziert (&amp;ldquo;digital&amp;rdquo;), was zu unerwünschten elektromagnetischen Störaussendungen sowohl am Netzein- als auch am Ausgang des FU führt.&lt;/p&gt;&#10;&lt;p&gt;Fast alle &lt;a href="https://blog.schallbert.de/portal-milling/#spindle-power"&gt;professionellen CNC-Spindeln&lt;/a&gt; sind 3-Phasen-AC-Asynchronmaschinen, die an einen Frequenzumrichter angeschlossen sind.&lt;/p&gt;&#10;&lt;p&gt;FUs haben oft einen standardisierten &lt;code&gt;0...10V&lt;/code&gt; Analogeingang zur Steuerung der Maschinendrehzahl. Sie besitzen zudem mehrere digitale (manchmal sogar potentialfreie) Ein- und Ausgänge, die z.B. von einem Melderelais der Schnittstellenkarte der numerischen Steuerung gespeist werden, und einen Nothalt- oder Fehlersignalausgang zur CNC-Schnittstelle. Sie können auch mit moderneren digitalen Bussystemen und analogen Ausgängen ausgestattet sein, was in meinem Einsatzbereich jedoch wegen der höheren Systemkosten selten genutzt wird.&lt;/p&gt;&#10;&lt;p&gt;Im Moment (Feb-2022) besitze ich keinen FU. Daher kenne ich keine Details zur Einstellung, Parametrierung und Anpassung an eine bestimmte Spindel, über die ich schreiben könnte.&lt;/p&gt;&#10;&lt;h3 id="pelv--dc-komponenten"&gt;PELV / DC-Komponenten&lt;/h3&gt;&#10;&lt;p&gt;Die typischen &amp;ldquo;kleinen&amp;rdquo; CNC-Schrittmotoren werden im PELV-Bereich (&lt;a href="https://en.wikipedia.org/wiki/Extra-low_voltage" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Protected Extra Low Voltage&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) mit Gleichstrom (DC) und unter &lt;code&gt;60V&lt;/code&gt; betrieben, so dass kein zusätzlicher Berührungsschutz erforderlich ist. Das &amp;ldquo;P&amp;rdquo; in PELV steht für &amp;ldquo;protected&amp;rdquo;, was bedeutet, dass die Geräte mit Erdungsleitungen verbunden sind. Auf diese Weise werden die Schaltkreise bei einem Kurzschluss gegen Erde durch auslösende Sicherungen abgeschaltet. Ein weiterer Vorteil der Erdung aller Komponenten ist die bessere Ableitung von Störeinflüssen, welche sonst die empfindliche Elektronik der CNC beeinträchtigen könnte.&lt;/p&gt;&#10;&lt;p&gt;Die Schrittmotortreiber, jeweils einer pro Achse, sind ebenfalls in der Box untergebracht. Sie sind in der Regel SELV (Safety Extra Low Voltage), so dass sie keine zusätzliche Erdung benötigen.&lt;/p&gt;&#10;&lt;p&gt;Dann gibt es noch eine Signalplatine, eine Breakout-Platine oder eine Relaisplatine, die Schrittmotorsignalzüge an die Treiber weiterleitet, Hilfsrelais steuert, Referenzschalter- und Notstopp-Eingänge ausliest und Signale für Spindel, Kühlschmierstoffzufuhr, Absaugung und/oder weitere Hilfssteuerungen ausgibt.&lt;/p&gt;&#10;&lt;p&gt;Ein Lüfter hält die Temperatur im Schaltkasten niedrig. Oft ist auch eine Schnittstellenkarte montiert, die direkt über die CNC-Software gesteuert wird, z. B. über USB oder Ethernet. Sie übersetzt und leitet Informationen an die einzelnen Komponenten der CNC und zurück an das Programm auf dem Computer, z. B. für Referenzschalter oder Not-Aus.&lt;/p&gt;&#10;&lt;h3 id="schrittmotortreiber-im-detail"&gt;Schrittmotortreiber im Detail&lt;/h3&gt;&#10;&lt;p&gt;Die Schnittstelle des Schrittmotortreibers ist recht einfach. Sie nimmt TTL-Pegel von &lt;code&gt;0V (low)&lt;/code&gt; und &lt;code&gt;5V (high)&lt;/code&gt; an und wird durch nur drei Eingänge definiert:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Signalname&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Beschreibung&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Enable (EN)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schaltet die Stromversorgung der Antriebe ein. Ermöglicht z.B. aktives Halten der Position&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Drehrichtung (DIR)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Auswahl der Motordrehrichtung&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Pulse (PUL)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lässt den Motor einen (Mikro-)Schritt drehen&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Interessanterweise werden dieselben drei Eingangssignale benötigt, um den Lesekopf eines Diskettenlaufwerks zu steuern. Ich habe dies in einem meiner früheren Projekte ausprobiert, einer Floppy-Bass-Orgel, das auf dem Softwareprojekt &lt;a href="https://blog.arduino.cc/2012/05/29/music-floppy-moppy/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;moppy&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; basiert.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-02-18_drivers.jpg" alt="Image: Stepper drivers, top view" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Intern interpretiert der Treiber die Eingangssignale und gibt Impulse aus, die für den Antrieb des Schrittmotors geeignet sind. Zwei H-Brücken-Kanäle - Schaltungen mit je vier Schalttransistoren zur Steuerung von Richtung und Höhe der Spannung an den Motorspulen - mit den Bezeichnungen &amp;ldquo;A&amp;rdquo; und &amp;ldquo;B&amp;rdquo; liefern Energie für die Polpaare des Schrittmotors.&lt;/p&gt;&#10;&lt;p&gt;Ausführlichere Informationen über Schrittmotorantriebe, Mikroschrittbetrieb und andere Aspekte finden Sie z. B. &lt;a href="https://blog.poscope.com/stepper-motor-driver/#What_is_a_stepper_motor_driver" target="_blank" rel="noopener noreferrer" class="external-link"&gt;hier&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; oder auf &lt;a href="http://www.schrittmotor-blog.de/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;diesem Blog&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 id="schrittmotoren"&gt;Schrittmotoren&lt;/h3&gt;&#10;&lt;p&gt;Schrittmotoren sind - wie 3-Phasen-Wechselstrom- oder bürstenlose Gleichstrommotoren - elektronisch kommutiert. Daher müssen dem Motor Strompulse zugeführt werden, damit er sich kontinuierlich dreht. Die meisten Schrittmotoren sind heutzutage &lt;a href="http://stepcontrol.com/pdf/step101.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Hybridmotoren&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Diese weisen einen mehrzahnigen Rotor auf, der in zwei Hälften geteilt ist welche um einen halben Zahn zueinander versetzt sind und jeweils von einer Spule gesteuert werden.&#10;Diese Art von Motor hat mindestens vier Anschlüsse, zwei für Spule &amp;ldquo;A&amp;rdquo; und zwei für Spule &amp;ldquo;B&amp;rdquo;.&#10;Bipolare Hybridmotoren haben sogar acht Leitungen, die die Spulen wiederum in zwei Teile aufteilen - auf diese Weise können sie in Reihe, parallel und auch unipolar geschaltet werden, so dass jede der vier Spulen einzeln gesteuert werden kann.&lt;/p&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-02-18_stepper-cable.jpg" alt="Image: Stepper cables" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Schrittmotoren werden meist in &amp;ldquo;bipolarer Parallelschaltung&amp;rdquo; an CNC-Schrittmotortreiber angeschlossen. Dabei werden die beiden Spulen eines Polpaares mit gleicher Ausrichtung parallel geschaltet. Die beiden Drähte dieser Parallelschaltung werden dann mit den Ausgängen &lt;code&gt;A+&lt;/code&gt; und &lt;code&gt;A-&lt;/code&gt; des Treibers verbunden. Diese Verdrahtung wird für Kanal &lt;code&gt;B&lt;/code&gt; wiederholt.&#10;Ich habe die zusammengehörenden Aderpaare mit einer Bohrmaschine verdrillt weil ich zu faul war, das von Hand zu machen. Verdrillen der spulengleichen Aderpaare &lt;code&gt;A/B&lt;/code&gt; macht Sinn, weil die mit Pulszügen angesteuerten Motoren erhebliche elektromagnetische Störungen erzeugen können. Durch das Verdrillen gleichen sich magnetische Felder von Hin- und Rückleitung aus.&lt;/p&gt;&#10;&lt;p&gt;Anders als bei Drehstrom- oder bürstenlosen Gleichstrommaschinen liegt die Priorität bei der Konstruktion von Schrittmotoren nicht auf einer möglichst hohen Energieeffizienz, geringen Geräuschemissionen, hohen Drehzahlen oder einem guten Wärmemanagement für den Dauerlauf, sondern auf hohem Drehmoment, großen Haltekräften zum Sichern einer Position und einem flüssigen Betrieb bei niedrigen Drehzahlen. Schrittmotoren sind für hohe Beschleunigungen gebaut und sollen stets der Impulsgeschwindigkeit folgen, da die Positionierung der Maschine davon abhängt.&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-02-18_stepper-connect.jpg" alt="Image: Stepper, connected to a driver in bipolar parallel configuration" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;p&gt;Schrittmotoren eignen sich wie Servomotoren auch zum Positionierbetrieb. Hierbei werden sie nicht über die Wellenposition geregelt, sondern die Position wird durch Zählen der Pulse in die jeweilige Richtung &amp;ldquo;geschätzt&amp;rdquo;. Solange kein Schrittverlust (durch Überlast oder zu hoch eingestellte Beschleunigungswerte) auftritt, ist diese Art der Positionssteuerung sowohl kostengünstig als auch hinreichend genau. Daher wird sie häufig verwendet.&lt;/p&gt;&#10;&lt;p&gt;Da die Positionsinformation bei Neustart der Maschine (meist) verloren geht, verfügen CNC-Maschinen mit Schrittmotoren über Referenzschalter an jeder Achse, die bei Inbetriebnahme angefahren werden und die Positionsinformation im Koordinatensystem der Maschine mit der der Schrittmotore verknüpft.&lt;/p&gt;&#10;&lt;h2 id="der-aufbau"&gt;Der Aufbau&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-02-18_switchbox-build.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Schaltkastenbau in neun Sekunden&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Der Bau des von mir gekauften Bausatzes für den Schaltkasten und die Verdrahtung nahm nur etwa 8 Stunden in Anspruch. Es erwies sich als entscheidend, alle Schaltpläne und Schritt-für-Schritt-Anleitungen im Voraus gründlich zu studieren. So konnte ich Fehler vermeiden, deren Behebung sonst Abende gekostet hätte.&lt;/p&gt;&#10;&lt;p&gt;An dem Bausatz gefällt mir, dass die Komponenten elektrisch aufeinander abgestimmt sind und alle notwendigen Leitungen, Buchsen (bis auf eine Ethernet-Buchse) und die Dokumentationen vollständig waren. Ich habe nur Kabelkanäle und einige Kabelschuhe vermisst, um das Kabelmanagement und die Verbindungen zum Gehäuse zu vereinfachen. Das Gehäuse ist sehr robust und alle Steckdosen passen gut an ihren Platz.&lt;/p&gt;&#10;&lt;p&gt;Am Frickeligsten beim Aufbau waren die Lötstellen der Schrittmotor/D-Sub-Stecker und deren Isolierung. Ebenso stellte es sich als schwierig heraus, die Steuerplatine im Gehäuse montiert zu bekommen.&lt;/p&gt;&#10;&lt;p&gt;Ich war wirklich aufgeregt, als ich die Steuerung zum ersten Mal einschaltete. Das Geräusch des anlaufenden Lüfters und das Blinken aller möglichen LEDs waren einen Seufzer der Erleichterung wert.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2022-02-18_switchbox-test.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Erster Test: Quittieren des Not-Aus Schaltkreises&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="sicherheitshinweise"&gt;Sicherheitshinweise&lt;/h3&gt;&#10;&lt;p&gt;Nein, ein geöffnetes netzbetriebenes Gerät sollte nicht eingeschaltet werden, wenn man nicht weiß, was man tut.&#10;Ja, ich habe in einen Fehlerstromschutzschalter installiert, um mich vor gefährlichen Spannungen zu schützen, und ja, ich habe alle Sicherungen doppelt überprüft, eine gründliche Sichtprüfung durchgeführt und Durchgangsmessungen an jedem einzelnen Anschluss vorgenommen, um Fehlverdrahtungen auszuschließen. Glücklicherweise bestand die Steuerung den ersten Funktionstest. Der Zustand des Notsignals wurde korrekt interpretiert und die LED der CNC-Platine blinkte mit der erwarteten &lt;code&gt;1Hz&lt;/code&gt;-Rate.&lt;/p&gt;&#10;&lt;h3 id="aufbau-einer-mini-control--serie-c1"&gt;Aufbau einer &amp;ldquo;Mini Control / Serie C1&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Für die Maker Faire Ruhr 2024, bei der ich einer der Aussteller war, habe ich eine weitere Steuerung aufgebaut: Die einfachere &amp;ldquo;Mini Control / Serie C1&amp;rdquo;. Ein Zeitraffervideo davon habe ich hier eingebettet:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;iframe src="https://makertube.net/videos/embed/hvZabGKMgP6QWeD5a8wgKg" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;CNC electronics assembly&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Vorteile:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Schnelle Montage&lt;/li&gt;&#10;&lt;li&gt;Kein Löten&lt;/li&gt;&#10;&lt;li&gt;Viele Snap-In-Teile&lt;/li&gt;&#10;&lt;li&gt;Kleines Gehäuse&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Nachteile:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Verbindung zur Maschine erfolgt über Schraubklemmen; Trennung zur Maschine nicht vorgesehen&lt;/li&gt;&#10;&lt;li&gt;Lauter Lüfter, auch im Leerlauf aktiv (kann aber mit „Schallberts Fanhack“ gelöst werden)&lt;/li&gt;&#10;&lt;li&gt;Mehrere externe Netzteile (Schrittmotorversorgung, 24V-Versorgung, Spindel-Schaltbox)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Mit der zugehörigen kleinen CNC-Fräse habe ich viel gemacht: &lt;a href="https://blog.schallbert.de/cnc-tuning-kinematics/"&gt;Tuning&lt;/a&gt; auf kürzere Fertigungszeiten, eine Spezialisierung für Gravuren und diverse Projekte wie Stempel, Dithering-Bilder und mehrfarbige &lt;a href="https://blog.schallbert.de/engrave-multicolor/"&gt;Carves&lt;/a&gt;.&lt;/p&gt;&#10;</description></item><item><title>CNC Teil1 - Portalfräsmaschine</title><link>https://blog.schallbert.de/portal-milling/</link><pubDate>Sat, 12 Feb 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/portal-milling/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-02-12_portalmillingmachine-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: My portal milling machine in mid-construction"&#10; title="CNC Teil1 - Portalfräsmaschine" /&gt;&#10;&lt;h2 id="cnc-portalfräsmaschine---für-anfänger"&gt;CNC Portalfräsmaschine - für Anfänger&lt;/h2&gt;&#10;&lt;p&gt;Es ist Winter 2021, und meine erste CNC-Maschine wird bald geliefert. Es wird ein Bausatz sein, der aus einer Menge Pakete und noch mehr Einzelteilen besteht. Ich habe die Maschine bestellt, weil ich Prototypen für &lt;a href="https://blog.schallbert.de/projects/aa_alpha_1_1/"&gt;AnywhereAmps&lt;/a&gt; schneller und genauer fertigen möchte. Und natürlich um ein paar weitere Hardware-Projekte umzusetzen, die mir schon länger im Kopf herumschwirren.&lt;/p&gt;&#10;&lt;h3 id="cnc"&gt;Moment mal&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-02-12_gcode.jpg" alt="G-code example" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Oh, Ok. CNC bedeutet &lt;a href="https://en.wikipedia.org/wiki/Numerical_control" target="_blank" rel="noopener noreferrer" class="external-link"&gt;computer numerical control&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und beschreibt, wie die Maschine gesteuert wird - nicht von Hand, sondern durch Instruktionen eines Computers.&lt;/p&gt;&#10;&lt;p&gt;Diese werden &lt;a href="https://en.wikipedia.org/wiki/G-code" target="_blank" rel="noopener noreferrer" class="external-link"&gt;G-Code&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; genannt, und sind in der häufig benutzten Interpretersprache RS274-NGC geschrieben, die Industriestandard für computergesteuerte Bearbeitungszentren geworden ist.&lt;/p&gt;&#10;&lt;p&gt;Eine Portalfräsmaschine ist einer Oberfräse nicht unähnlich. Der Fräsmotor ist auf einem Gestell montiert, das ihn in (meist) drei Achsen X, Y, Z mithilfe von &lt;a href="https://en.wikipedia.org/wiki/Stepper_motor" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Schrittmotoren&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; oder &lt;a href="https://en.wikipedia.org/wiki/Servomotor" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Servomotoren&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; bewegt. Ein Rechner steuert diese Bewegungen und ist so in der Lage, nahezu beliebige Formen zu schneiden. Die Kinematik der Portalfräsmaschine ist ähnlich zu der von &lt;a href="https://en.wikipedia.org/wiki/3D_printing#Processes_and_printers" target="_blank" rel="noopener noreferrer" class="external-link"&gt;3D-Druckern&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, obwohl sie natürlich ganz unterschiedliche Anforderungen an die Mechanik haben - eine Portalfräsmaschine muss viel stabiler, steifer und schwerer konstruiert werden, da die Rahmenkonstruktion sämtliche Schneid- und Beschleunigungskräfte aufnehmen muss.&lt;/p&gt;&#10;&lt;p&gt;Natürlich sind CNC-Maschinen nicht auf 3D-Drucker und Fräsmaschinen beschränkt. Es gibt CNC-&lt;a href="https://en.wikipedia.org/wiki/Lathe" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Drehmaschinen&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Plotter" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Plotter&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Laser_cutting" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Laserschneider&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Water_jet_cutter" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Wasserstrahlschneider&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und viele mehr. Streng genommen sind sogar Industrieroboter CNC-Maschinen.&lt;/p&gt;&#10;&lt;h2 id="fragen"&gt;Fragen&lt;/h2&gt;&#10;&lt;p&gt;Dies sind einige der Fragen, die ich hatte, bevor ich dieses ziemlich teure Stück Technik gekauft habe. Einige davon lassen sich leicht in ein paar Zeilen beantworten, andere würden wahrscheinlich einen ganzen Artikel für sich allein füllen.&lt;/p&gt;&#10;&lt;h3 id="cnc-fräsmaschine"&gt;CNC-Fräsmaschine&lt;/h3&gt;&#10;&lt;h4 id="beschleunigt-sie-wirklich-meinen-prototyping-prozess"&gt;Beschleunigt sie wirklich meinen Prototyping-Prozess?&lt;/h4&gt;&#10;&lt;p&gt;Ich weiß es noch nicht, aber ich hoffe es. Die Holzarbeiten für einen einzigen Prototyp nehmen etwa 8 Stunden meiner Zeit in Anspruch. Am meisten Zeit verschlingen die Vorbereitungen für den Zuschnitt, die Messungen und das Abpausen. Allein der Lautsprecheradapter von AnywhereAmps Alpha mit seiner gewölbten Oberfläche nimmt mehr als eine Stunde Fertigungszeit in Anspruch.&lt;/p&gt;&#10;&lt;p&gt;Mit einer CNC-Portalfräsmaschine hoffe ich, mehr wie ein Plätzchenbäcker zu werden: Alle Teile für AnywhereAmps (mit Ausnahme des zylindrischen Gehäuses) in einem einzigen Stück Sperrholz zu verschachteln, die Maschine die Arbeit machen zu lassen und nach einer Weile nur noch minimale Nacharbeiten beim Ausschneiden und Schleifen der Teile zu erledigen.&lt;/p&gt;&#10;&lt;p&gt;Und wenn ich einen weiteren Prototyp oder andere Lautsprecherdurchmesser haben möchte: Ich passe einige Parameter in meinem CAD-Tool an, stelle die Werkzeugwege neu ein, schicke sie an den Computer der Maschine, spanne ein neues Stück Sperrholz ein und drücke auf &amp;ldquo;Auftrag ausführen&amp;rdquo;. Fertig (in der idealen Welt).&lt;/p&gt;&#10;&lt;p&gt;Andererseits braucht das Einarbeiten in die ganzen Software-Tools auch seine Zeit. Mein Einführungsartikel über &lt;a href="https://blog.schallbert.de/freecad-get-started/"&gt;FreeCAD hier verlinkt&lt;/a&gt; ist ein Beispiel dafür.&lt;/p&gt;&#10;&lt;h4 id="mehr-geld-für-portalhardware-oder-lieber-für-den-spindelantrieb-ausgeben"&gt;Mehr Geld für Portalhardware oder lieber für den Spindelantrieb ausgeben?&lt;/h4&gt;&#10;&lt;p&gt;Das ist eine Frage an die Experten, die ich selbst erst beantworten kann, wenn ich schon eine Weile eine CNC besitze. Ich habe mich dafür entschieden, mehr Geld für das Portal und weniger für die Spindel auszugeben, und zwar aus mehreren Gründen, die sich aus dem Bauch heraus ergeben:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Das Portal ist meine Maschine, die Spindel nur ein Teil davon.&lt;/li&gt;&#10;&lt;li&gt;Die Spindel ist günstiger, leichter auszutauschen und bei weitem nicht so aufwendig zu montieren wie das Portal, also möchte ich, dass das Portal eine Konstante ist.&lt;/li&gt;&#10;&lt;li&gt;Wenn ich durch die Fähigkeiten meiner Portalfräsmaschine eingeschränkt bin, müsste ich zuerst herausfinden, warum, und dann an einzelnen Komponenten herumschrauben, um sie zu verbessern. Wenn die Spindel der limitierende Faktor ist, kaufe ich mir einfach eine stärkere und gut ist.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h4 id="spindle-power"&gt;Wie viel Spindelleistung?&lt;/h4&gt;&#10;&lt;p&gt;Das ist meine erste schwierige Frage. Ich bin mir fast sicher, dass ich für meine Holzbearbeitung kein 2,2-kW-Monster brauche. Um anständige Vorschubgeschwindigkeiten zu erzielen und genügend Spielraum für Dichteunregelmäßigkeiten wie Astlöcher zu haben, könnte es eine gute Idee sein, sich im Leistungssegment der Handoberfräse zwischen 700W und 1200W zu orientieren, was mir immer gute Dienste geleistet hat. Ich habe mich für eine 1000W &lt;a href="https://en.wikipedia.org/wiki/Universal_motor#cite_note-tm-6" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Universalmotor&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Maschine entschieden.&lt;/p&gt;&#10;&lt;p&gt;Im Gegensatz zu den oft als &amp;ldquo;HF-Spindeln&amp;rdquo; (HF = Hochfrequenz) bezeichneten Drehstrommotoren benötigen sie keinen &lt;a href="https://blog.schallbert.de/cnc-electronics/#vfd"&gt;Frequenzumrichter&lt;/a&gt; und keine externe Kühlung, da ihr Lüfter auf die Welle geschraubt ist. Andererseits sind sie in der Regel lauter (der Lüfter ist nicht unabhängig von der Motordrehzahl) und weniger effizient (Lüfterverluste, Kommutierungsverluste, Kupferverluste, Magnetisierungsverluste usw.) Ich habe im Internet nur grobe Zahlen zum Wirkungsgrad gefunden, also nehmen wir mal an:&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;1000W&lt;/code&gt; Universalmotorantrieb &lt;code&gt;*30%&lt;/code&gt; Gesamtwirkungsgrad inkl. Elektronik &amp;amp; Lüfter: &lt;code&gt;300W&lt;/code&gt; @ Motorwelle&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;700W&lt;/code&gt; &lt;a href="https://www.scribd.com/document/776268309/2-4-6polig-e-M-asynchron-IP44-IP56" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Asynchronmaschine&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; &lt;code&gt;*85%&lt;/code&gt; Wirkungsgrad &lt;code&gt;*90%&lt;/code&gt; &lt;a href="http://www.variablefrequencydrive.org/vfd-efficiency" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Wirkungsgrad Steuerung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; &lt;code&gt;*97%&lt;/code&gt; external cooling: &lt;code&gt;520W&lt;/code&gt; @ motor shaft&lt;/p&gt;&#10;&lt;p&gt;Das wäre ein gewaltiger Unterschied, so dass sich eine spätere Aufrüstung doch lohnen könnte. Außerdem reichen für eine HF-Spindel im Vergleich zu einem konventionellen Antrieb geringere Wattzahlen aus.&lt;/p&gt;&#10;&lt;h4 id="tool-changer"&gt;Automatischer Werkzeugwechsler - ja oder nein?&lt;/h4&gt;&#10;&lt;p&gt;Das hängt wohl davon ab, was ich brauche. Ich kaufe mir zunächst einen einfachen Fräsmotor mit digitaler Drehzahlregelung als Spindel. Ein &amp;ldquo;richtiger&amp;rdquo; Werkzeugwechsler - richtig bedeutet, dass ich auch Werkzeugschäfte von &lt;code&gt;10mm&lt;/code&gt; und größer montieren kann und gewuchtete Spannzangen, 3-Phasen-Antrieb mit eigenständiger Kühlung und Frequenzumrichter habe - würde ~10 mal so viel kosten. Außerdem kostet jedes ER20-Spannzangenfutter für den Werkzeugwechsler mal eben die Hälfte meines Fräsmotors. Davon bräuchte ich mehr als eine, wenn der Werkzeugwechsler Sinn machen soll. Und ich bräuchte eine Druckluftanlage mit Schaltkasten usw. Das alles zusammen würde die Kosten für die Portalfräse locker überschreiten!&lt;/p&gt;&#10;&lt;p&gt;Schlussfolgerung: Wenn ich die CNC in Zukunft sehr oft benutzen werde und wenn meine Projekte unbedingt häufige Werkzeugwechsel zum Schruppen, Glätten, Fasen, 3D usw. erfordern, dann könnte ich diesen teuren Schritt in Betracht ziehen. Vielleicht. Irgendwann.&lt;/p&gt;&#10;&lt;h4 id="welche-größe-ist-die-beste"&gt;Welche Größe ist die beste?&lt;/h4&gt;&#10;&lt;p&gt;Idealerweise könnte man für die Bearbeitung von Sperrholzplatten eine ganze Standardplatte von &amp;ldquo;1250mm x 2500mm&amp;rdquo; oder einen Bruchteil davon, z.B. &amp;ldquo;610mm x 1250mm&amp;rdquo;, auflegen. Aber selbst die kleineren Platten würden nicht in den Keller meiner kleinen Wohnung passen. Also entschied ich mich für eine mit &lt;code&gt;840mm x 740mm&lt;/code&gt; Auflagefläche.&lt;/p&gt;&#10;&lt;p&gt;Für Ihre Wahl: Prüfen Sie, ob Sie platzmäßig eingeschränkt sind. Wenn nicht, dann machen Sie sich gründlich Gedanken darüber, was Sie mit Ihrer Maschine machen wollen. Die Größe der Maschine ergibt sich automatisch, wenn Sie wissen, wofür Sie sie hauptsächlich einsetzen wollen.&lt;/p&gt;&#10;&lt;h4 id="plug-and-play-oder-ein-bausatz"&gt;Plug-and-play oder ein Bausatz?&lt;/h4&gt;&#10;&lt;p&gt;Dies hängt von mehreren Aspekten ab: Von Budget, der Zeit, die man zu investieren bereit ist, und natürlich davon, ob man zumindest ein bisschen Talent im Umgang mit Schraubendrehern, Muttern, Abisolierzangen und dem Lötkolben hat.&#10;Es kommt auch darauf an, ob einem jede Schraube Ihrer Maschine persönlich bekannt sein soll.&lt;/p&gt;&#10;&lt;h4 id="kugelumlaufspindeln-zum-bewegen-der-achsen"&gt;Kugelumlaufspindeln zum Bewegen der Achsen?&lt;/h4&gt;&#10;&lt;p&gt;Flinke Eilgänge, hohe Wiederholgenauigkeit durch reduziertes oder eliminiertes Spiel oder höhere Vorschubgeschwindigkeiten? Fräsen im Gleichlauf, um die Oberflächenqualität und die Standzeit der Werkzeuge zu erhöhen? Wenn Sie einen dieser Punkte bejahen, sollten Sie das zusätzliche Geld für Kugelumlaufspindeln ausgeben.&lt;/p&gt;&#10;&lt;h4 id="zusätzliche-vielseitigkeit"&gt;zusätzliche Vielseitigkeit?&lt;/h4&gt;&#10;&lt;p&gt;Wie im Abschnitt &lt;a href="https://blog.schallbert.de/portal-milling/#cnc"&gt;Moment mal&lt;/a&gt; beschrieben, wäre das Portal selbst auch für andere Zwecke geeignet. Es gibt Schleppmesser zum Plotten, 3D-Druckköpfe und sogar Gravurlaser, die bei Bedarf an das Portal angeflanscht werden könnten.&lt;/p&gt;&#10;&lt;p&gt;Um sich darauf vorzubereiten, ist eine Spindel, die auf den Euronorm-Hals mit einem Durchmesser von 43 mm passt möglicherweise die beste Wahl, da die meisten anderen Werkzeuge auch für diese Passform erhältlich sind. Dies erleichtert einen eventuellen Umbau. Wobei andererseits die meisten Maschinen, die etwas taugen, spezialisiert sind auf ihr Fach - ein 3D-Drucker schafft wahnwitzige Beschleunigungsvorgänge, der Plotter spart beim Platzverbrauch und die CNC-Fräse ist nunmal wegen der Schneidkräfte stabiler gebaut.&lt;/p&gt;&#10;&lt;h3 id="allgemeine-fragen"&gt;Allgemeine Fragen&lt;/h3&gt;&#10;&lt;h4 id="entsteht-dabei-viel-staub"&gt;Entsteht dabei viel Staub?&lt;/h4&gt;&#10;&lt;p&gt;Ja. Besorgen Sie sich eine gute Staubabsaugung. Ich würde ein Zyklonsystem oder einen dieser riesigen Textilbeutel empfehlen, die normalerweise bei stationären Sägen verwendet werden. Wenn Sie Ihre Maschine regelmäßig benutzen, reicht die Aufnahmekapazität eines Werkstattsaugers allein möglicherweise nicht aus.&lt;/p&gt;&#10;&lt;p&gt;Außerdem sollten Sie den Staubsauger gut erden (z. B. indem Sie die Erdungsleitung(en) mit dem Erdungspunkt Ihres Schaltkastens verbinden) und antistatische Saugschläuche kaufen, die Sie an die Erdung des Staubsaugers oder des Staubbehälters anschließen können.&#10;Auf diese Weise vermeiden Sie, dass Sie jedes Mal, wenn Sie vakuumbezogene Dinge berühren, einen leichten Schlag bekommen, und Sie können einen schwer auszumachenden Grund dafür ausschließen, dass Ihre Maschine verrückt spielt, z. B. plötzliches Einfrieren der Software, zufälliges Erkennen von Not-Aus-Ereignissen durch die Steuerung, Falschauslösung eines Referenzschalters oder sogar Schluckauf bei Schrittmotoren. Es gibt viele Fälle in verschiedenen Foren, in denen dies die Ursache war oder gewesen sein könnte, z. B. &lt;a href="https://cambamcnc.com/forum/index.php?topic=4524.0" target="_blank" rel="noopener noreferrer" class="external-link"&gt;hier&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h4 id="ist-sie-laut"&gt;Ist sie laut?&lt;/h4&gt;&#10;&lt;p&gt;Ja. Der Geräuschpegel ist vergleichbar mit dem einer Handoberfräse. Sie ist schon im Leerlauf laut, und es wird noch schlimmer, wenn sie durch Material fährt. Es könnte also eine gute Idee sein die leiseren, aber teureren Hochfrequenzspindeln mit externer Kühlung zu kaufen. Darüber hinaus sollte (&lt;a href="https://eur-lex.europa.eu/legal-content/DE/TXT/PDF/?uri=CELEX:02006L0042-20091215&amp;amp;rid=1" target="_blank" rel="noopener noreferrer" class="external-link"&gt;und je nach den örtlichen Vorschriften sogar muss&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) Ihre Maschine aus Sicherheitsgründen, aber auch zur Lärmminderung, eine Einhausung erhalten. Sie hilft auch dabei, Feinstaub in der Raumluft zu reduzieren.&lt;/p&gt;&#10;&lt;p&gt;Die Geräuschentwicklung hängt aber auch stark vom verwendeten Fräser ab. Meiner Erfahrung nach klingen kurze Planfräser mit großem Durchmesser und 10-mm-Schaft &lt;a href="https://blog.schallbert.de/my-endmill-screams/"&gt;angenehmer&lt;/a&gt; und sind vielleicht sogar weniger laut als Fräser mit Fischschwanz oder besonders langem Schaft.&lt;/p&gt;&#10;&lt;h4 id="wie-sieht-der-arbeitsablauf-aus"&gt;Wie sieht der Arbeitsablauf aus?&lt;/h4&gt;&#10;&lt;p&gt;Hier ist eine vereinfachte Darstellung des Arbeitsablaufs mit einer CNC-Maschine.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2022-02-12_cncworkflow.jpg" alt="Bild: CNC-Arbeitsablauf in Kürze" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ähnlich wie beim Handwerk beginnt alles mit einer Idee. Die Zeichnungen und Bemaßungen der Teile werden nicht auf einem leeren Blatt Papier, sondern in einem Computer Aided Design-Tool erstellt. Die Zeichnung oder 3D-Modelldatei wird dann in einem Computer Aided Manufacturing Tool weiterverarbeitet, wo geeignete Fräswerkzeuge ausgewählt, die Frässtrategien festgelegt und die Werkzeugwege erzeugt werden. Die resultierende G-Code-Datei wird dann an die eigentliche CNC Software gesendet, die die Schrittmotortreiber steuert. Diese senden Stromimpulse an die Schrittmotoren, die wiederum die Maschine mechanisch verfahren, um das Teil zu fertigen.&lt;/p&gt;&#10;&lt;h3 id="fragen-zum-betrieb"&gt;Fragen zum Betrieb&lt;/h3&gt;&#10;&lt;h4 id="milling-style"&gt;Fräsen im Gleich- oder Gegenlauf?&lt;/h4&gt;&#10;&lt;p&gt;Dies ist eine Entscheidung, die in der CAM-Software getroffen wird.&lt;/p&gt;&#10;&lt;p&gt;Fräsen im Gegenlauf = der Fräser &amp;ldquo;schnitzt&amp;rdquo; in das Material, seine Schneiden drehen sich entgegen der Materialtransportrichtung. Der Gegenlauf wird bei allen Kreissägen verwendet die ich kenne, um Rückschläge zu vermeiden oder das Werkstück auf dem Tisch zu halten.&#10;Gleichlauffräsen = der Fräser &amp;ldquo;frisst&amp;rdquo; sich in das Material, seine Schneiden rotieren mit der Transportrichtung. Der Fräser neigt dazu, sich selbst in das Material zu ziehen.&#10;Weitere Informationen z.B. auf &lt;a href="https://www.harveyperformance.com/in-the-loupe/conventional-vs-climb-milling/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;harveryperformance&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; oder &lt;a href="https://blog.tormach.com/climb-milling-versus-conventional-milling-sneaky-cnc-tricks" target="_blank" rel="noopener noreferrer" class="external-link"&gt;tormachs blog&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Für welche der beiden Möglichkeiten Sie sich entscheiden sollten, hängt von der Steifigkeit und dem Gewicht Ihrer Maschine, den Drehmoment Ihres Schrittmotors, der Leistung Ihrer Schrittmotorantriebe und dem Material ab, das Sie bearbeiten. Wenn Sie eine steife, schwere, leistungsstarke Maschine (mit Kugelumlaufspindeln) haben und mit eher weichen Materialien arbeiten, sollten Sie das Gleichlauffräsen verwenden. Beobachten Sie Ihre Maschine genau, um zu sehen, ob sich alles glatt anfühlt und anhört (!). Im Zweifelsfall sollten Sie im Gegenlauf fräsen. Arbeiten Sie bei handgeführten Oberfräsen immer den Gegenlauf!&lt;/p&gt;&#10;&lt;p&gt;Nachtrag: Ich fräse Dibond im Gegenlauf mit guten Ergebnissen, Acrylglas und Holz jedoch bearbeite ich im Gleichlauf.&lt;/p&gt;&#10;&lt;h4 id="wie-spannt-man-das-werkstück-schnell-und-einfach-auf"&gt;Wie spannt man das Werkstück schnell und einfach auf?&lt;/h4&gt;&#10;&lt;p&gt;Das hängt wiederum von Ihrem Material ab:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Große Platten: Vakuumtisch oder Anschlag mit Klemmen, die die Platte gegen den Anschlag drücken&lt;/li&gt;&#10;&lt;li&gt;Kleine Platten: Klemmen oder Verschraubung auf dem Fräsbett&lt;/li&gt;&#10;&lt;li&gt;Kleine Blöcke: Schraubstock&lt;/li&gt;&#10;&lt;li&gt;Unregelmäßige Formen: Klemmen, die das Material auf dem Fräsbett niederhalten&lt;/li&gt;&#10;&lt;li&gt;Beidseitiges Fräsen: projektspezifische Vorrichtungen, die das Material in Position halten&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Hmm, die &lt;a href="https://blog.schallbert.de/projects/cnc_spoilboard/"&gt;Opferplatte mit Anschlag&lt;/a&gt; wird wahrscheinlich eins meiner ersten Projekte mit der neuen Maschine.&lt;/p&gt;&#10;&lt;h4 id="mit-welchen-bits--fräsern-sollte-man-beginnen"&gt;Mit welchen Bits / Fräsern sollte man beginnen?&lt;/h4&gt;&#10;&lt;p&gt;Ich würde für den Anfang die folgenden allgemeinen Fräser empfehlen. Sie sollten natürlich in Herstellung, Spiralform, Nutenzahl usw. zum verwendeten Material passen.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Klassischer Schaftfräser mit &lt;code&gt;6mm&lt;/code&gt; oder 1/4&amp;quot;, Spirale mit &lt;code&gt;15-30°&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;2mm&lt;/code&gt; Schaftfräser mit Freischnitt für feines Material oder kleine Löcher&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;60°&lt;/code&gt; und &lt;code&gt;90°&lt;/code&gt; Gravierfräser für Gravuren und Fasen&lt;/li&gt;&#10;&lt;li&gt;Planfräser für großflächiges Fräsen&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h4 id="mein-schwerpunkt-ist-holz-was-sind-geeignete-vorschübe-und-drehzahlen"&gt;Mein Schwerpunkt ist Holz, was sind geeignete Vorschübe und Drehzahlen?&lt;/h4&gt;&#10;&lt;p&gt;Birkensperrholz ist hart aber nachgiebig. Die geeigneten Werte berücksichtigen immer die Stabilität der Maschine, die Drehmomente der Schrittmotoren und die Spindelleistung.&lt;/p&gt;&#10;&lt;p&gt;Wichtig ist, dass der Fräser Späne erzeugt und keinen Staub. Deshalb sollten niedrige Vorschubgeschwindigkeiten bei hohen Spindeldrehzahlen vermieden werden. Für den Anfang in Holz: Verwenden Sie die Hälfte Ihres Werkzeugdurchmessers als Tiefe pro Durchgang, berechnen Sie die Spindeldrehzahl mit der Formel für Vorschübe und Drehzahlen oder einem Rechner aus dem Internet, und probieren Sie diese aus. Schauen Sie sich die Späne an. Hören Sie der Maschine bei der Arbeit zu. Hört sie sich &amp;ldquo;gut&amp;rdquo; an? Erhöhen Sie die Tiefe pro Durchgang oder die Vorschubgeschwindigkeit allmählich und hören Sie an dem Punkt auf, an dem entweder Ihre Spindel gestresst klingt, Ihr Finish schlechter wird oder Sie sich mit Ihrer Maschine nicht mehr sicher fühlen. Drehen Sie wieder eine Stufe zurück, und das könnte Ihre ideale Vorschubgeschwindigkeit und Durchgangstiefe für das Material und den Fräser sein, den Sie gerade verwenden.&lt;/p&gt;&#10;&lt;p&gt;Für meine Maschine habe ich mich für &lt;code&gt;F3500mm/min&lt;/code&gt; = &lt;code&gt;138in/m&lt;/code&gt; bei &lt;code&gt;S24000U/min&lt;/code&gt; und einer Tiefenzustellung in Höhe des Fräsdurchmessers &lt;code&gt;Z+6mm&lt;/code&gt;, Bahnüberlappung &lt;code&gt;10%&lt;/code&gt; entschieden. Für die Recherche habe ich die folgenden Websites benutzt: &lt;a href="https://www.cnccookbook.com/feeds-speeds-cnc-wood-cutting/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;cnccookbook&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://www.cncrechner.de/rechner/vorschub/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;cnc-rechner&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; sowie Webseiten von Werkzeugherstellern wie &lt;a href="https://webseite.sorotec.de/download/fraesparameter/schnittwerte.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Sorotec&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 id="weiterlesen"&gt;Weiterlesen?&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/portal-milling-build/"&gt;CNC: Der Aufbau&lt;/a&gt;&#10;&lt;a href="https://blog.schallbert.de/cnc-electronics/"&gt;CNC Elektronik&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>Einschaltstrombegrenzer Teil2</title><link>https://blog.schallbert.de/inrushlimiter-circuits/</link><pubDate>Thu, 10 Feb 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/inrushlimiter-circuits/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/inrushlimiter-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Inrush limiter circuit variant"&#10; title="Einschaltstrombegrenzer Teil2" /&gt;&#10;&lt;h2 id="inhalt"&gt;Inhalt&lt;/h2&gt;&#10;&lt;p&gt;In diesem Artikel konzentriere ich mich auf zwei Möglichkeiten, eine Halbleiter-Einschaltstrombegrenzerschaltung zu erstellen: Die eine kopiert den Relais-Ansatz aus &lt;a href="https://blog.schallbert.de/knowledge-inrushlimiters/"&gt;Elektronikwissen: Einschaltstrombegrenzer&lt;/a&gt;, wobei lediglich das Relais durch einen p-Kanal-MOSFET ersetzt und eine besser definierte Zeitverzögerung hinzugefügt wird. Zum anderen stelle ich einen &amp;ldquo;linearen&amp;rdquo; Ansatz vor, bei dem die Stromversorgung schrittweise durch einen PNP-Leistungstransistor eingeschaltet wird.&lt;/p&gt;&#10;&lt;h2 id="motivation-einschaltstrombegrenzer-für-usb-pd"&gt;Motivation: Einschaltstrombegrenzer für USB-PD&lt;/h2&gt;&#10;&lt;p&gt;Da meine Experimente mit &lt;a href="https://blog.schallbert.de/usbpd-explained/"&gt;USB-PD für AnywhereAmps&lt;/a&gt; vorerst fehlgeschlagen sind, muss ich eine Schaltung erstellen, die den Einschaltstrom in meinem Verstärker begrenzt, bevor ich es erneut versuche.&lt;/p&gt;&#10;&lt;h2 id="schaltungen-erläutert"&gt;Schaltungen erläutert&lt;/h2&gt;&#10;&lt;p&gt;Die beiden Schaltungen sind sich sehr ähnlich. Sie unterscheiden sich eigentlich nur in zwei Komponenten des Leistungsteils. Im Allgemeinen wird die Verzögerungszeit durch eine Vergrößerung des Kondensators &lt;code&gt;C4&lt;/code&gt; oder eine Vergrößerung des Ladewiderstands &lt;code&gt;R9&lt;/code&gt; verlängert.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/inrush_circuit.jpg" alt="Image: inrush current limiter sections" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="eingangsbereich-blau"&gt;Eingangsbereich (blau)&lt;/h3&gt;&#10;&lt;p&gt;Der Eingangsteil verfügt über eine &lt;a href="https://blog.schallbert.de/knowledge-tvsdiodes/"&gt;TVS-Diode&lt;/a&gt;, um Verpolung oder Überspannung zu vermeiden.&lt;/p&gt;&#10;&lt;h3 id="verzögerungsabschnitt-gelb"&gt;Verzögerungsabschnitt (gelb)&lt;/h3&gt;&#10;&lt;p&gt;Nach dem Einschalten wird &lt;code&gt;C4&lt;/code&gt; über &lt;code&gt;R9&lt;/code&gt; allmählich aufgeladen. Wenn die Schwellenspannung des NPN-Bipolartransistors &lt;code&gt;Q2&lt;/code&gt; erreicht ist, wird die Spannung hinter &lt;code&gt;R11&lt;/code&gt; langsam reduziert, was wiederum &lt;code&gt;M2&lt;/code&gt;, den Leistungspfad dieser Schaltung, aktiviert. &lt;code&gt;R12&lt;/code&gt; wurde hinzugefügt, um einen stabilen Arbeitspunkt für &lt;code&gt;Q2&lt;/code&gt; zu schaffen.&lt;/p&gt;&#10;&lt;h3 id="leistungsteil-grün"&gt;Leistungsteil (grün)&lt;/h3&gt;&#10;&lt;p&gt;Die lineare Variante hat einen PNP-Leistungstransistor &lt;code&gt;Q1&lt;/code&gt; im Hauptstrompfad (&lt;a href="https://blog.schallbert.de/inrushlimiter-circuits/#schaltungsvergleich"&gt;Bild unten&lt;/a&gt;). Er wird über &lt;code&gt;Q2&lt;/code&gt; aktiviert, und der Strom, den er liefern kann, wird über &lt;code&gt;R10&lt;/code&gt; gesteuert, der in diesem Fall einen relativ niedrigen Wert hat um hohe Emitter-Kollektor-Ströme zu ermöglichen.&lt;/p&gt;&#10;&lt;p&gt;Während die schaltende Variante einen Strombegrenzungswiderstand &lt;code&gt;R8&lt;/code&gt; verwendet, der durch den p-Kanal-Mosfet &lt;code&gt;M2&lt;/code&gt; kurzgeschlossen wird, erfordert seine spannungsgesteuerte Natur keine besondere Sorgfalt bei der Auswahl von &lt;code&gt;R10&lt;/code&gt;. Man sollte nur darauf achten, dass seine (negative) Gate-Schwellenspannung sicher eingehalten wird.&lt;/p&gt;&#10;&lt;h3 id="simulierte-last-violett"&gt;Simulierte Last (violett)&lt;/h3&gt;&#10;&lt;p&gt;Meine Last ist die Endstufenschaltung. Um den Ruhezustand und das Einschaltverhalten zu simulieren, habe ich einen &lt;code&gt;400Ohm&lt;/code&gt;-Widerstand &lt;code&gt;R5&lt;/code&gt; und einen gepolten &lt;code&gt;1200uF&lt;/code&gt;-Kondensator &lt;code&gt;C3&lt;/code&gt; gewählt. Die TVS-Diode schützt vor Überspannung und Rückströmen. Ich habe die Simulationen mit Kondensatorwerten von bis zu &lt;code&gt;10000uF&lt;/code&gt; und Lastwiderständen bis herunter auf &lt;code&gt;40Ohm&lt;/code&gt; ausprobiert, alle mit ähnlichen und guten Ergebnissen.&lt;/p&gt;&#10;&lt;h2 id="schaltungsvergleich"&gt;Schaltungsvergleich&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/inrushlimiter-variants.jpg" alt="Image: Einschaltstrombegrenzer-Varianten im Vergleich" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die dunkelblaue Linie im Diagramm ist wichtig: Sie beschreibt die Ausgangsspannung des Systems im Zeitverlauf. Beide Schaltungen sind so abgestimmt, dass sie die Ziel-Ausgangsspannung nach 0,2 Sekunden erreichen. Dennoch unterscheiden sich die Kurven ziemlich stark, was die unterschiedliche Natur der beiden Ansätze widerspiegelt.&lt;/p&gt;&#10;&lt;h3 id="p-ch-mosfet-ansatz"&gt;P-CH MOSFET Ansatz&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Aufgrund des Schaltvorgangs ist die Einschaltstrombegrenzung zwar wirksam, aber nicht ganz gleichmäßig&lt;/li&gt;&#10;&lt;li&gt;die Leistungsverluste sind bei dieser Variante sehr gering, da sowohl der auslösende NPN-Transistor als auch der Leistungs-MOSFET für sich genommen nicht viel Strom verbrauchen.&lt;/li&gt;&#10;&lt;li&gt;breiter Einsatzbereich für angeschlossene Geräte, der nur durch den maximalen Dauerstrom des MOSFETs begrenzt wird.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="pnp-leistungstransistor-ansatz"&gt;PNP-Leistungstransistor-Ansatz&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;sehr ruhiger Betrieb, die Einschaltzeit kann aufgrund des großen ohmschen Bereichs des PNP sehr gut gesteuert werden&lt;/li&gt;&#10;&lt;li&gt;höhere Leistungsverluste im eingeschalteten Zustand aufgrund des hohen Basisstroms durch &lt;code&gt;R10&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Nicht sehr vielseitig, da hohe Lastströme einen beträchtlichen Spannungsabfall über den Emitter-Kollektor-Pfad von &lt;code&gt;Q1&lt;/code&gt; erzeugen.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="weitere-verbesserungen"&gt;Weitere Verbesserungen&lt;/h3&gt;&#10;&lt;p&gt;Um die Vielseitigkeit dieser Schaltung zu behalten und gleichzeitig eine glatte Spannungskurve zu erreichen, könnte man einen Operationsverstärker verwenden, um den Laststrom zu überwachen und ihn bei Bedarf auf die zulässigen Werte herunterzuregeln. Der Leistungs-MOSFET könnte beibehalten werden, während der Nachteil des engem ohmschen Betriebsbereichs verschwinden würde. Der Preis dafür wäre zusätzliche Komplexität, wie sie &lt;a href="https://electronics.stackexchange.com/questions/408379/current-limiting-source-using-opamp" target="_blank" rel="noopener noreferrer" class="external-link"&gt;in diesem Stackexchange-Beitrag&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; beschrieben wird.&lt;/p&gt;&#10;</description></item><item><title>Energieversorgung per USB</title><link>https://blog.schallbert.de/usbpd-explained/</link><pubDate>Thu, 27 Jan 2022 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/usbpd-explained/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/stusb4500-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: ST&amp;#39;&amp;#39;s EVAL-SCS001V1: STUSB4500 evaluation board"&#10; title="Energieversorgung per USB" /&gt;&#10;&lt;h2 id="usb-power-delivery"&gt;USB Power Delivery&lt;/h2&gt;&#10;&lt;p&gt;USB Power Delivery ist ein Standard innerhalb des USB 3.x-Protokolls, der die Stromversorgung und die Spannungspegel innerhalb des USB-Netzwerks festlegt.&#10;Mit USB-PD ist es möglich, Verbraucher in Reihe zu schalten, die dann automatisch den Strombedarf mit der/den Quelle(n) aushandeln. Der Standard hat verschiedene Versionen, von denen die neueste (2021) bis zu 5 Ampère bei &lt;code&gt;3,3...48V&lt;/code&gt; und maximal 240 Watt Leistung zulässt.&lt;/p&gt;&#10;&lt;h3 id="usb-pd-rollen"&gt;USB-PD-Rollen&lt;/h3&gt;&#10;&lt;p&gt;Im USB-Power-Delivery-Standard gibt es verschiedene Rollen, die die Teilnehmer an den Enden eines USB-Kabels spielen können. Sie können Host oder Client für Datenzwecke und gleichzeitig Stromquelle, Stromsenke oder beides für andere Teilnehmer sein.&lt;/p&gt;&#10;&lt;h3 id="rollenbegriffe"&gt;Rollenbegriffe&lt;/h3&gt;&#10;&lt;p&gt;USB-C verwendet das Akronym &lt;code&gt;DFP&lt;/code&gt; oder &amp;ldquo;Down-Facing Port&amp;rdquo;, um zu verdeutlichen, dass es sich um den Port eines &lt;a href="https://community.infineon.com/t5/Knowledge-Base-Articles/USB-Type-C-and-USB-PD-FAQs/ta-p/679219#." target="_blank" rel="noopener noreferrer" class="external-link"&gt;Host&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; handelt. &lt;code&gt;UFP&lt;/code&gt; oder &amp;ldquo;Up-Facing Port&amp;rdquo; wird für Clients verwendet, die mit Hosts verbunden werden können. Es gibt aber noch eine dritte Rolle: &lt;code&gt;DRP&lt;/code&gt; oder &amp;ldquo;Dual-Role Port&amp;rdquo;. Dieser Anschluss kann entweder als Host oder als Client dienen, je nachdem, wer der Verbindungspartner auf der anderen Seite der Leitung ist und was das USB-Netzwerk aushandelt.&lt;/p&gt;&#10;&lt;p&gt;Im USB-PD-Kontext kann jeder dieser Ports so konfiguriert werden, dass er als Stromquelle oder Stromsenke dient. Beispiel: Obwohl die Rolle des USB-C-Anschlusses eines Laptops auf &amp;ldquo;DFP&amp;rdquo; konfiguriert sein kann, kann er, wenn er an eine Stromquelle angeschlossen ist, immer noch eine Stromsenke sein. Hmm.&lt;/p&gt;&#10;&lt;p&gt;In diesem Beitrag verwende ich nur die &amp;ldquo;UFP&amp;rdquo;-Rolle sowohl im Sinne des USB-C- als auch des USB-PD-Wortes. Also versuche ich, eine Stromsenke für mein Gerät zu konfigurieren, damit es die benötigte Eingangsspannung und den Strom erhält.&lt;/p&gt;&#10;&lt;h3 id="usb-pd-version"&gt;USB-PD Version&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Vor USB-PD Standard 2.0 unterstützten USB-C-Geräte nur sogenannte &lt;em&gt;Power Profiles&lt;/em&gt; mit festen Spannungen von 5V, 12V, 20V und Strömen von bis zu 2A, 3A, oder 5A.&lt;/li&gt;&#10;&lt;li&gt;Zwischen USB-PD Standard 2.0 und USB-PD 3.0 haben &lt;em&gt;Power Delivery Objects&lt;/em&gt; die alten Power Profiles ersetzt, die es erlauben, verschiedene Spannungen/Stromstärken von der Senke auszuwählen &lt;a href="https://usb.org/sites/default/files/D2T2-1%20-%20USB%20Power%20Delivery.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;je nach Angebot der Stromquelle&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Die Spannungen sind auf 5V, 9V, 15V, 20V festgelegt, mit Strombegrenzungen von 2.25A, 3A, 5A, je nach Fähigkeit der Quelle und Spannungseinstellung. &lt;code&gt;PDO&lt;/code&gt;s erlauben Fallback-Strategien, d. h. wenn eine Quelle die höchste Leistungsanforderung nicht unterstützen kann, kann die Senke ein anderes &lt;code&gt;PDO&lt;/code&gt; mit geringerer Spannung/Strom auswählen, das sowohl für die Quelle als auch für die Senke geeignet ist.&lt;/li&gt;&#10;&lt;li&gt;Mit USB-PD Standard 3.0 &lt;code&gt;PPS&lt;/code&gt; bietet das Protokoll mit &lt;em&gt;Programmable Power Supply&lt;/em&gt; noch mehr Flexibilität. Hier fordert eine Stromsenke einen Spannungspegel zwischen &lt;code&gt;3,3V&lt;/code&gt; und &lt;code&gt;21V&lt;/code&gt; an, der in &lt;code&gt;20mV&lt;/code&gt;-Schritten und 0,2 bis 5A in &lt;code&gt;50mA&lt;/code&gt;-Schritten wählbar ist, wobei die volle Rückwärtskompatibilität zu den Power Delivery Objects von USB-PD 2.0 erhalten bleibt.&lt;/li&gt;&#10;&lt;li&gt;Der neueste USB-PD Standard 3.1 erweitert sogar die Grenzen der programmierbaren Stromversorgungen und erlaubt 3,3V - 48V und bis zu 5A &lt;a href="https://www.thephonetalks.com/usb-pd-2-0-vs-3-0-vs-3-1/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;mit AVS&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; - &lt;em&gt;Adaptable Voltage Files&lt;/em&gt;. Puh.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Nicht alle Quellen (Steckdosen, Powerbanks usw.) können alle Spannungspegel oder Stromstärken liefern oder haben überhaupt nur den gleichen Standard in der Umsetzung. Aber eine Reihe von Werten ist ziemlich üblich: 5V, 9V, 12V, 15V, 20V. Einige der Produkte, die ich auf dem Markt gesehen habe, bieten bis zu 4A, aber die meisten halten es mit bis zu 3A und Spannungen bis zu 15V sind am häufigsten (im Jahr 2021).&lt;/p&gt;&#10;&lt;h2 id="asics-für-die-stromversorgung"&gt;ASICs für die Stromversorgung&lt;/h2&gt;&#10;&lt;p&gt;ASIC steht für Application Specific Integrated Circuit (anwendungsspezifischer integrierter Schaltkreis), und in diesem Beitrag versuche ich, ASICs mit der Funktion der Stromsenke zu beleuchten, die eigenständig arbeiten können, d. h. ohne ein externes Steuergerät wie einen Mikroprozessor. Ich habe drei Typen dieser ICs gefunden:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;USB-PD-Senke, Spannungs- und Stromeinstellung über eine Widerstandskaskade&lt;/li&gt;&#10;&lt;li&gt;USB-PD-Senke, Spannungs- und Stromkennlinie über den Datenbus programmierbar (&lt;a href="https://en.wikipedia.org/wiki/I%C2%B2C" target="_blank" rel="noopener noreferrer" class="external-link"&gt;I2C&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;/&lt;a href="https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter" target="_blank" rel="noopener noreferrer" class="external-link"&gt;UART&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;/&lt;a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface" target="_blank" rel="noopener noreferrer" class="external-link"&gt;SPI&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) in den nicht-flüchtigen Speicher des ASIC.&lt;/li&gt;&#10;&lt;li&gt;USB-PD-Senke mit integriertem Mikroprozessor, der programmiert werden muss&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Was alle ASICs gemeinsam haben: Sie benötigen periphere Schaltungen, um zu funktionieren - zumindest einige Widerstände, Kondensatoren und Leistungstransistoren, um die Stromversorgung zu schalten und mit angeschlossenen Stromquellen zu verhandeln.&lt;/p&gt;&#10;&lt;h3 id="usb-pd-ist-komplex-versionierung-und-vertragsverhandlungen"&gt;USB-PD ist komplex: Versionierung und Vertragsverhandlungen&lt;/h3&gt;&#10;&lt;p&gt;Sowohl die Abwärtskompatibilität als auch die Skalierbarkeit von USB-PD fordern ihren Tribut - die USB-PD-Spezifikation ist ein mehr als 600 Seiten starkes Monster und es ist nicht immer klar, welche Quelle oder Senke welche Protokollebene unterstützt.&lt;/p&gt;&#10;&lt;p&gt;Da sowohl die Quelle, die Senke als auch das Kabel einen Power-Delivery-Vertrag aushandeln müssen, der auf den Anforderungen der Senke, den Stromversorgungsoptionen der Quelle und den Möglichkeiten des Kabels basiert, kann man sich vorstellen, dass dieser Prozess sehr komplex ist. Dies gilt umso mehr, als dass alle Beteiligten möglicherweise einen anderen USB-PD-Standard, eine andere Revision oder eine andere Version implementiert haben. Kleiner Fakt am Rande: Der Zip-Ordner des USB-Power-Delivery-Spezifikationspakets auf der &lt;a href="https://usb.org/document-library/usb-power-delivery" target="_blank" rel="noopener noreferrer" class="external-link"&gt;offiziellen Seite&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; hat eine Dateigröße von &lt;code&gt;&amp;gt;42MB&lt;/code&gt;. Obwohl ich dieses Thema für sehr interessant halte, habe ich nicht die Zeit, mich in die Einzelheiten zu vertiefen 😅.&lt;/p&gt;&#10;&lt;p&gt;Lassen wir also all dieses Zeug hinter uns und versuchen, eine einfach zu implementierende Lösung zu finden.&lt;/p&gt;&#10;&lt;h3 id="beispiel-asics"&gt;Beispiel-ASICs&lt;/h3&gt;&#10;&lt;p&gt;Ich habe willkürlich drei ICs aus dem Internet ausgewählt, die von verschiedenen Firmen angeboten werden, jeweils einen pro Gerätetyp:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Cypress/Infinions &lt;a href="https://www.infineon.com/cms/en/product/universal-serial-bus-usb-power-delivery-controller/usb-c-and-power-delivery/ez-pd-barrel-connector-replacement-bcr/cypd3177-24lqxqt/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;CYPD3177&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, ein USB-PD-Sink-Controller mit Widerstandskonfiguration&lt;/li&gt;&#10;&lt;li&gt;STMicroelectronics&amp;rsquo; &lt;a href="https://www.st.com/en/interfaces-and-transceivers/stusb4500.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;STUSB4500&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, ein USB-PD-Sink-Controller mit Speicher-Konfiguration&lt;/li&gt;&#10;&lt;li&gt;Microchips &lt;a href="https://www.microchip.com/en-us/product/UPD301C#document-table" target="_blank" rel="noopener noreferrer" class="external-link"&gt;UPD301C&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, ein voll programmierbarer USB-PD-Dual-Role-Controller&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Ich werde mir zuerst den STUSB4500 genauer anschauen.&lt;/p&gt;&#10;&lt;h3 id="chipkrise--ic-knappheit"&gt;Chipkrise / IC-Knappheit&lt;/h3&gt;&#10;&lt;p&gt;Für die Integration in meinen Entwurf wollte ich einige Muster kaufen. Leider war die Chip-Knappheit so groß, dass keiner der Distributoren, bei denen ich normalerweise Elektronikteile bestelle, für das nächste Jahr welche auf Lager hatte.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/digikey_stusb4500.jpg" alt="Image: STUSB4500 bei DigiKey nicht auf Lager" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/st_stusb4500.jpg" alt="Image: STUSB4500 bei ST nicht mehr vorrätig" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/farnell_stusb4500.jpg" alt="Image: STUSB4500 bei Farnell vergriffen" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/mouser_stusb4500.jpg" alt="Image: STUSB4500 bei Mouser nicht mehr lieferbar" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Deshalb habe ich mich entschlossen, eine Entwicklungsplatine zu bestellen, auf der der ASIC bereits montiert ist. Das ist zwar 10x teurer als der Chip selbst, aber hier hatte ich keine Wahl.&lt;/p&gt;&#10;&lt;h3 id="stusb4500"&gt;Integration eines STUSB4500&lt;/h3&gt;&#10;&lt;p&gt;Der STUSB4500 verwendet den USB-PD-Standard 2.0 - also nicht den neuesten und besten - aber die Abwärtskompatibilität mit späteren Standards macht ihn zu einem schnellen und einfachen Ausgangspunkt.&#10;Das Gerät wird mit drei Power Delivery Objects vorprogrammiert:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;PDO1 = 5V/1.5A&lt;/li&gt;&#10;&lt;li&gt;PDO2 = 15V/1,5A&lt;/li&gt;&#10;&lt;li&gt;PDO3 = 20V/1A&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Für meine &lt;a href="https://blog.schallbert.de/projects/aa_alpha_1_1/"&gt;AnywhereAmps Alpha 1.1&lt;/a&gt; evolution 1.2 kann ich das Ding also ohne Neuprogrammierung sofort verwenden. Wenn ich die Power-Delivery-Objekte ändern wollte, würde ich ein Arduino-Board mit I2C verwenden und die STUSB4500-Bibliothek installieren, die z. B. unter &lt;a href="https://registry.platformio.org/libraries/ardnew/STUSB4500" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Platformio&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; leicht erhältlich ist. Damit könnte ich sogar die programmierbare Stromversorgung von USB-PD 3.0 aktivieren, wenn ich wollte. Mal sehen, wenn ich die Zeit finde, werde ich einen speziellen Blogeintrag für die Programmierung des STUSB4500 vorbereiten.&lt;/p&gt;&#10;&lt;h3 id="einschalten-des-verstärkers"&gt;Einschalten des Verstärkers&lt;/h3&gt;&#10;&lt;p&gt;OK, also habe ich das Eval-Board an ein 15V-fähiges 30V USB-C-Netzteil angeschlossen. Die violette LED zeigt an, dass 15V auf der Ausgangsschiene verfügbar sind.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/stusb4500_on.jpg" alt="Image Usb-PD Evaluation Board STUSB4500 stock, 15V mode" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;p&gt;Jetzt habe ich meine Verstärkerschaltungen an den Ausgangsklemmen für einen ersten Test angeschlossen. Die STUSB-Auswertungsplatine leuchtet nicht mehr, Verstärker aus. Meh. Wenigstens habe ich einen Hinweis darauf, was die Ursache sein könnte: Es sind &lt;a href="https://blog.schallbert.de/capacitor-problems/"&gt;wieder kapazitive Einschaltströme&lt;/a&gt;! Um Geräte mit großen Kondensatoren mit Strom versorgen zu können, muss ich entweder den Strom im PDO auf abnorm hohe Werte einstellen, oder ich entscheide mich für die Konstruktion einer geeigneten, miniaturisierten Einschaltstrombegrenzerschaltung. Ich habe bereits einen Prototyp aufgebaut, den ich &lt;a href="https://blog.schallbert.de/knowledge-inrushlimiters/"&gt;hier&lt;/a&gt; vorstelle.&lt;/p&gt;&#10;&lt;h2 id="sollten-sie-usb-pd-für-ihre-projekte-verwenden"&gt;Sollten Sie USB-PD für Ihre Projekte verwenden?&lt;/h2&gt;&#10;&lt;p&gt;Die Antwort lautet eindeutig &amp;ldquo;es kommt darauf an&amp;rdquo;. Obwohl die Möglichkeiten von USB-PD großartig sind, gibt es ein paar Nachteile:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Es wird ein zusätzlicher ASIC mit Peripherie benötigt, um die &amp;ldquo;Verträge&amp;rdquo; auszuhandeln. Dies benötigt Platz auf der Leiterplatte und erhöht die Projektkosten erheblich.&lt;/li&gt;&#10;&lt;li&gt;Um vollständig abwärtskompatibel zu sein, ist es sinnvoll, einen ASIC zu kaufen, der sowohl den Standard 3.x unterstützt, als auch idealerweise programmierbar ist.&lt;/li&gt;&#10;&lt;li&gt;Wenn Sie einen programmierbaren Baustein nehmen, gibt es eine zusätzliche Lernkurve für die Softwareentwicklung, die ASIC-spezifischen Bibliotheken, die Sie verwenden werden, und möglicherweise sogar die Notwendigkeit für Programmierhardware&lt;/li&gt;&#10;&lt;li&gt;Sie müssen sicherstellen, dass die Stromversorgung und das Kabel mit den Anforderungen Ihrer USB-PD-Senke kompatibel sind. Zudem sind Kabel für hohe Ströme teuer&lt;/li&gt;&#10;&lt;li&gt;Der USB-C-Anschluss ist miniaturisiert. SMD-Löten und Einsteiger-Lötstationen werden wohl nie Freunde werden, daher solltest etwas Erfahrung in diesem Bereich mitgebracht werden.&lt;/li&gt;&#10;&lt;li&gt;Hochstecker sind viel einfacher in einem Gehäuse zu montieren als eine Platine mit aufgesetzter USB-C-Buchse, die zudem starken mechanischen Belastungen standhalten muss.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Andererseits ist die Verwendung von USB-PD sehr sinnvoll, wenn&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;ein mobiles Gerät betrieben werden soll, das mitgenommen oder häufig an verschiedenen Stromquellen betrieben werden soll&lt;/li&gt;&#10;&lt;li&gt;die Flexibilität batteriebetriebener Geräte gewünscht ist: Einfach eine kompatible Powerbank anschließen und los geht&amp;rsquo;s&lt;/li&gt;&#10;&lt;li&gt;ohnehin eine Datenverbindung zu Ihrem Gerät benötigt wird&lt;/li&gt;&#10;&lt;li&gt;besondere Anforderungen an Ihre Stromversorgung bestehen und ganz bestimmte Spannungen/Ströme geliefert werden sollen&lt;/li&gt;&#10;&lt;li&gt;ein einfacher Überstrom- und Überspannungsschutz von Haus aus gewünscht ist&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Metabo / Cordless Alliance Systems</title><link>https://blog.schallbert.de/no-can-use-metabo/</link><pubDate>Thu, 09 Dec 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/no-can-use-metabo/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/metabo-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Metabo logo, image courtesy of Metabo"&#10; title="Metabo / Cordless Alliance Systems" /&gt;&#10;&lt;h2 id="energiequelle-für-anywhereamps"&gt;Energiequelle für AnywhereAmps&lt;/h2&gt;&#10;&lt;h3 id="status-quo"&gt;Status quo&lt;/h3&gt;&#10;&lt;p&gt;Ich habe Metabo / CAS (&lt;a href="https://cordless-alliance-system.de/de" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Cordless Alliance System&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) Batterien für beide AnywhereAmps &lt;a href="https://blog.schallbert.de/projects/aa_alpha/"&gt;Alpha 1. 0&lt;/a&gt; und &lt;a href="https://blog.schallbert.de/projects/aa_alpha_1_1/"&gt;Alpha 1.1&lt;/a&gt;. Letzteres verwendet bereits den Power Supervisor IC &lt;a href="https://blog.schallbert.de/configure-max1756x/"&gt;MAX17562&lt;/a&gt;, der sowohl die Batterie als auch die Schaltkreise vor Verpolung, Überstrom und Unterspannung schützt.&lt;/p&gt;&#10;&lt;h3 id="problem"&gt;Problem&lt;/h3&gt;&#10;&lt;p&gt;Obwohl die Verwendung des Power-Supervisor-ICs ein großer Fortschritt war, wäre es noch besser, wenn ich den &lt;a href="https://en.wikipedia.org/wiki/State_of_charge" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Ladezustand der Batterie&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; direkt ablesen könnte. Dies würde nicht nur einen sicheren Betrieb gewährleisten, sondern auch ein automatisches Abschalten des Verstärkers ermöglichen, wenn die Batterie schwach wird.&lt;/p&gt;&#10;&lt;h2 id="diskussion-mit-metabocas"&gt;Diskussion mit Metabo/CAS&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/metabo.jpg" alt="Metabo logo" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Metabo logo&lt;/span&gt;&lt;a href="https://www.metabo.com/de/de/info/aktuell/media/logos/" class="attr-link" aria-label="Attribution 1"&gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Deshalb habe ich mich an das Metabo-CAS-Team gewandt und gefragt, ob ich Unterstützung und die Spezifikationen für die Kommunikation mit dem Akkupack bekommen kann.&#10;Zu meiner großen Freude reagierten sie sehr schnell, und schon bald befand ich mich in einem Telefongespräch um die Angelegenheit zu klären.&lt;/p&gt;&#10;&lt;h3 id="ergebnis"&gt;Ergebnis:&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Die Verwendung von CAS-Akkus in anderen Geräten als der Elektrowerkzeug-Familien der CAS gilt als &amp;ldquo;Hack&amp;rdquo; und wird daher von Metabo nicht genehmigt.&lt;/li&gt;&#10;&lt;li&gt;Metabo ist daran interessiert, dass Hersteller von Akku-Werkzeugen ihrer Akku-Allianz beitreten, aber meine Anwendung in einem Verstärker ist einfach zu weit weg (Metabos Slogan war einmal &lt;em&gt;&amp;ldquo;Work, don&amp;rsquo;t play&amp;rdquo;&lt;/em&gt;)&lt;/li&gt;&#10;&lt;li&gt;Metabo würde von mir erwarten, dass ich mehr als 10.000 Einheiten pro Jahr produziere, bevor ich der Allianz beitreten kann - was einfach unrealistisch ist.&lt;/li&gt;&#10;&lt;li&gt;Ich müsse dies verstehen, denn die gemeinsame Nutzung der Spezifikationen und die Erlaubnis, die Schnittstellen zu nutzen, würde einen Teil von Metabos Ressourcen in Anspruch nehmen, während sie keine Rendite erwarten könnten&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;So enttäuschend 😔 dies auch ist, kam es nicht völlig unerwartet. Der Ingenieur am Telefon nahm sich Zeit für mich. Er sagte mir, dass sie meine Begeisterung für ihre Batteriesysteme sehr zu schätzen wüssten und dass ich mich nicht entmutigen lassen solle.&lt;/p&gt;&#10;&lt;p&gt;Nun ja. Sprechen wir über eine&lt;/p&gt;&#10;&lt;h2 id="alternative"&gt;Alternative.&lt;/h2&gt;&#10;&lt;p&gt;Ich kenne nur einen weiteren Standard, der alle meinen Ansprüchen gerecht zu werden scheint:&lt;/p&gt;&#10;&lt;h3 id="anforderungen"&gt;Anforderungen&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&amp;ldquo;offen genug&amp;rdquo; für meinen Gebrauch&lt;/li&gt;&#10;&lt;li&gt;sicher&lt;/li&gt;&#10;&lt;li&gt;erfüllt die Anforderungen an Spannung und Stromstärke für meinen Zweck&lt;/li&gt;&#10;&lt;li&gt;weithin verfügbar&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/USB_Type-C_Charging_Logo.svg/330px-USB_Type-C_Charging_Logo.svg.png" alt="USB-PD logo" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;USB-PD logo&lt;/span&gt;&lt;a href="https://commons.wikimedia.org/wiki/File:USB_Type-C_Charging_Logo.svg#file" class="attr-link" aria-label="Attribution 2"&gt;&#10; &lt;sup class="attr-id"&gt;[2]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="usb-power-delivery"&gt;USB Power Delivery&lt;/h3&gt;&#10;&lt;p&gt;Der Standard heißt &lt;a href="https://en.wikipedia.org/wiki/USB_hardware#USB_power_delivery" target="_blank" rel="noopener noreferrer" class="external-link"&gt;USB Power Delivery&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. Obwohl ich schon früher darüber nachgedacht habe, wollte ich ihn nie wirklich nutzen, da er einen enormen zusätzlichen Entwicklungsaufwand für mich bedeutet.&lt;/p&gt;&#10;&lt;p&gt;USB Power Delivery verwendet ein komplexes Protokoll, um einen &amp;ldquo;Vertrag&amp;rdquo; zwischen Quell- und Senkengerät zu schließen. All dies muss designt, ein spezieller IC ausgewählt und Schaltungen müssen dafür entworfen werden, damit es mit meinem Setup funktioniert.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-12-09_st_stusb4500.jpg" alt="Image: STUSB4500 Bildbeispiel für USB-PD-Sink-IC" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;😱 Oh nein, sie sind nicht mehr auf Lager!&lt;/p&gt;&#10;&lt;p&gt;Ich werde einen separaten Blog-Beitrag schreiben, sobald ich dieses neue Thema in den Griff bekommen habe.&lt;/p&gt;&#10;</description></item><item><title>Konfiguration des IC MAX1756x</title><link>https://blog.schallbert.de/configure-max1756x/</link><pubDate>Sat, 04 Dec 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/configure-max1756x/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-12-04_max1756x-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Max1756x typicall application, image courtesy of Maxim Integrated"&#10; title="Konfiguration des IC MAX1756x" /&gt;&#10;&lt;h2 id="power-supervisor-konfiguration-leicht-gemacht"&gt;Power Supervisor Konfiguration leicht gemacht&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-12-04_max17562.jpg" alt="Image: MAX17562 auf meiner Leiterplatte für AnywhereAmps" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich verwende MAX17562 ICs in meinem Design für &lt;a href="https://blog.schallbert.de/anywhereamps-get-pcb/"&gt;AnywhereAmps Alpha 1.1&lt;/a&gt;. Daher habe ich eine kurze Zusammenfassung der MAX1756x-Funktionen und deren Konfiguration erstellt 🙂.&lt;/p&gt;&#10;&lt;p&gt;Das Datenblatt ist die Grundlage für alle Informationen, die ich hier bereitstelle. Ich füge lediglich einige Überlegungen zur Gestaltung, Details zu meiner Art der Nutzung und Anwendungsvorschläge hinzu.&lt;/p&gt;&#10;&lt;h3 id="was-ist-dieser-chip-nochmal"&gt;Was ist dieser Chip nochmal?&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://www.maximintegrated.com/en/products/power/protection-control/protection-ics/MAX17561.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;MAX1756x&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist ein einstellbarer Überspannungs-/Unterspannungs- und Überstromschutz-IC. Er ist in drei verschiedenen Versionen für die Behandlung von Überstromereignissen erhältlich:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;MAX17561: Schaltet sich bei einem Überstromereignis aus und stellt sich nach typischerweise &lt;code&gt;600ms&lt;/code&gt; selbsttätig ab.&lt;/li&gt;&#10;&lt;li&gt;MAX17562: Schaltet sich bei einem Überstromereignis ab; Neustart nach Freigabezyklus&lt;/li&gt;&#10;&lt;li&gt;MAX17563: Setzt den normalen Betrieb mit aktivem Strombegrenzer fort (führt Wärme ab)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Beide Versionen begrenzen Überstromereignisse auf die konfigurierte Stromgrenze.&lt;/p&gt;&#10;&lt;h3 id="mein-max1756x-anwendungstool"&gt;Mein MAX1756x-Anwendungstool&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-12-04_spreadsheet.jpg" alt="Image: Max1756x-Anwendungsrechner-Tabelle" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Um die Verwendung des Max1756x IC in Ihrer Anwendung zu vereinfachen, können Sie meine &lt;a href="https://github.com/Schallbert/AnywhereAmps/blob/master/Preamp/max1756x_application_calculator.xlsx" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Max1756x Kalkulationstabelle&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; verwenden.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&#10;&lt;h3 id="max1756x-strombegrenzer--überlastdetektor"&gt;MAX1756x Strombegrenzer / Überlastdetektor&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-12-04_seti.jpg" alt="SETI resistor configuration from MAX1756x datasheet" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Courtesy of Maxim Integrated: SETI resistor config&lt;/span&gt;&lt;a href="https://www.analog.com/media/en/technical-documentation/data-sheets/MAX17561-MAX17563.pdf" class="attr-link" aria-label="Attribution 1"&gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Der IC verfügt über einen einstellbaren Strombegrenzer und eine Überstromerkennung (&lt;code&gt;SETI&lt;/code&gt;). Wenn der Strom über den &lt;code&gt;SETI&lt;/code&gt;-Schwellenwert ansteigt, wird er auf den eingestellten Wert begrenzt. Für MAX17561 und MAX17562: Bleibt diese Bedingung länger als die typische &lt;code&gt;20.7ms&lt;/code&gt; Tastzeit bestehen, schaltet der Baustein den Lastkreis ab. Da der MAX17563 seinen normalen Betrieb fortsetzt, kann seine Verlustleistung irgendwann zu einer Übertemperaturabschaltung führen.&lt;/p&gt;&#10;&lt;p&gt;So konfiguriert man den Strombegrenzer / die Überlasterkennung über den Eingang &lt;code&gt;SETI&lt;/code&gt;:&#10;Einen Widerstand entsprechend der Kurve auf der rechten Seite (aus dem Datenblatt) auswählen und verbinden mit &lt;code&gt;SETI&lt;/code&gt; und &lt;code&gt;GND&lt;/code&gt; verbinden. Für AnywhereAmps Alpha 1.2 verwende ich einen &lt;code&gt;3k3&lt;/code&gt;-Widerstand, um eine Stromgrenze von &lt;code&gt;~3.5A&lt;/code&gt; zu erreichen.&lt;/p&gt;&#10;&lt;h3 id="max1756x-unterspannungskonfiguration"&gt;MAX1756x Unterspannungskonfiguration&lt;/h3&gt;&#10;&lt;p&gt;Der IC verfügt über eine Unterspannungsabschaltung (&lt;code&gt;UVLO&lt;/code&gt;). Wenn die Spannung für etwa &lt;code&gt;16,7ms&lt;/code&gt; unter den &lt;code&gt;UVLO&lt;/code&gt;-Schwellenwert sinkt, schalten die Leistungstransistoren des Bausteins den Lastkreis ab. Intern vergleicht der IC die Eingangsspannung am &lt;code&gt;UVLO&lt;/code&gt;-Pin mit der Referenzspannung &amp;ldquo;Vbg = 1,2V&amp;rdquo;. Um den Schwellenwert für die Unterspannungsabschaltung einzustellen, verwenden Sie einen Spannungsteiler, um den &lt;code&gt;UVLO&lt;/code&gt;-Pegel während des normalen Betriebs über &lt;code&gt;Vbg&lt;/code&gt; zu halten.&lt;/p&gt;&#10;&lt;h3 id="max1756x-überspannungskonfiguration"&gt;MAX1756x Überspannungskonfiguration&lt;/h3&gt;&#10;&lt;p&gt;Der IC verfügt über eine Überspannungsabschaltung (&lt;code&gt;OVLO&lt;/code&gt;). Wenn die Spannung für etwa &lt;code&gt;16,7ms&lt;/code&gt; über den &lt;code&gt;OVLO&lt;/code&gt;-Schwellenwert steigt, schalten die Leistungstransistoren des Bausteins den Lastkreis ab.&#10;Die Funktionsweise ist identisch mit der Unterspannungsabschaltung, mit dem einzigen Unterschied, dass die Spannung am &lt;code&gt;OVLO&lt;/code&gt;-Pin unter normalen Betriebsbedingungen unter &lt;code&gt;Vbg&lt;/code&gt; bleiben sollte.&lt;/p&gt;&#10;&lt;h3 id="max1756x-verpolschutz"&gt;MAX1756x Verpolschutz&lt;/h3&gt;&#10;&lt;p&gt;Der Verpolschutz des ICs kann durch Ziehen des &lt;code&gt;*RIEN&lt;/code&gt;-Pins nach &lt;code&gt;GND&lt;/code&gt; aktiviert werden. Soll diese Funktion genutzt werden ist zu beachten, dass ungepolte Bypass-Kondensatoren am Eingang des ICs verwendet werden müssen. Nutzt man &lt;a href="https://blog.schallbert.de/capacitor-problems/#explosion"&gt;wie ich es getan habe&lt;/a&gt; Elkos, so explodieren diese, sollte die Betriebsspannung verpolt werden.&lt;/p&gt;&#10;&lt;h3 id="fehler-flag-ausgang"&gt;Fehler *FLAG Ausgang&lt;/h3&gt;&#10;&lt;p&gt;Der MAX1756x zeigt unter den folgenden Bedingungen einen Stromversorgungsfehler über seinen &lt;code&gt;*FLAG&lt;/code&gt;-Port an:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Thermische Überlast&lt;/li&gt;&#10;&lt;li&gt;Überspannungsereignis definiert durch &lt;code&gt;OVLO&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Verpolung: wenn der Verpolschutz &lt;code&gt;*RIEN&lt;/code&gt; aktiviert wurde&lt;/li&gt;&#10;&lt;li&gt;Überlaststrom, definiert durch &lt;code&gt;SETI&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="empfehlung-tvs-dioden-verwenden"&gt;Empfehlung: TVS-Dioden verwenden&lt;/h3&gt;&#10;&lt;p&gt;Wie das Datenblatt empfiehlt und &lt;a href="https://blog.schallbert.de/capacitor-problems/#inductance"&gt;meine Erfahrung aus früheren Vorfällen&lt;/a&gt; zeigt, würde ich &lt;code&gt;27V&lt;/code&gt; &lt;a href="https://blog.schallbert.de/knowledge-tvsdiodes/"&gt;Transient Voltage Suppressor diodes&lt;/a&gt; parallel zu den beiden Eingangs- und Ausgangsports des MAX1756x verwenden. Sie schützt (alle induktiven) Lastschaltungen vor negativen Spannungsspitzen beim Abschalten und den IC selbst vor Überspannungen beim Anschluss einer spannungsführenden Stromversorgung. Außerdem begrenzt er die Eingangsspannung in umgekehrter Richtung auf unbedenkliche Werte.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-12-04_tvs.jpg" alt="Image: TVS-Verwendung in MAX1756x-Schaltung" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;h3 id="empfehlung-2-verwenden-sie-nicht-polarisierte-eingangskondensatoren"&gt;Empfehlung 2: Verwenden Sie nicht polarisierte Eingangskondensatoren&lt;/h3&gt;&#10;&lt;p&gt;Um Probleme beim Neustart des IC zu vermeiden, wenn er an &amp;ldquo;schwache&amp;rdquo; Stromversorgungen oder hochohmige Eingangsleitungen angeschlossen ist, sollten größere nicht-polarisierte Eingangskondensatoren als üblich verwendet werden (nein, &amp;ldquo;100nF&amp;rdquo; sind eher nicht genug!) - insbesondere, wenn kapazitive oder andere Lasten betrieben werden, die Eingangsstoßströme verursachen.&lt;/p&gt;&#10;&lt;p&gt;Für weitere Informationen zur Vermeidung von Problemen in diesem Bereich bitte in meinen Blog-Eintrag über &lt;a href="https://blog.schallbert.de/knowledge-inrushlimiters/"&gt;Einschaltstrombegrenzer&lt;/a&gt; weiterstöbern.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;Zu beachten: Obwohl ich das Datenblatt gründlich gelesen und die Daten sorgfältig hinzugefügt habe, kann ich natürlich keinerlei Garantie für die Ergebnisse übernehmen, die Sie mit diesem Tool erzielen.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Einschaltstrombegrenzer - Teil1</title><link>https://blog.schallbert.de/knowledge-inrushlimiters/</link><pubDate>Sun, 14 Nov 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/knowledge-inrushlimiters/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-11-14_bulb-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Electrical light bulb, 12V 5W"&#10; title="Einschaltstrombegrenzer - Teil1" /&gt;&#10;&lt;h2 id="elektronikwissen-einschaltstrombegrenzer"&gt;Elektronikwissen: Einschaltstrombegrenzer&lt;/h2&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2021-11-14_inrush_current_limiter_relay.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt; Einschaltstrombegrenzer in Aktion.&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="was-ist-das"&gt;Was ist das?&lt;/h3&gt;&#10;&lt;p&gt;Ein &lt;a href="https://en.wikipedia.org/wiki/Inrush_current" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Einschaltstrom&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ist der Stromstoß, der direkt nach dem Einschalten durch einen Stromkreis fließt, bis der Stromkreis seinen stabilen Zustand erreicht hat. Hohe Einschaltströme sind oft unerwünscht, da sie Schaltungsteile überlasten und deren Lebensdauer verkürzen können, elektromagnetische Störungen verursachen und einen kurzzeitigen Spannungsabfall bewirken, der zu ständigen Neustarts eines gesteuerten Geräts &lt;a href="https://blog.schallbert.de/capacitor-problems/"&gt;wie hier beschrieben&lt;/a&gt; oder zu anderem unerwünschten Verhalten führen kann.&lt;/p&gt;&#10;&lt;h3 id="beispiel"&gt;Beispiel&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://upload.wikimedia.org/wikipedia/commons/f/f9/Lamp_inrush_current.png" alt="Wikipedia: inrush current of an incandescent light bulb" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Oszillograf: Einschaltstrom einer Lampe mit Glühfaden&lt;/span&gt;&lt;a href="https://commons.wikimedia.org/wiki/File:Lamp_inrush_current.png#file" class="attr-link" aria-label="Attribution 1"&gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Glühlampe: Die rosa Linie ist die Ausgangsspannung des Netzteils, die gelbe Linie zeigt den Strom. Man kann erkennen, dass das Netzteil die Ausgangsspannung verringert, um die Stromaufnahme während des Starts zu reduzieren. Nach etwa &lt;code&gt;100ms&lt;/code&gt; stabilisiert sich die Spannung, da der Stromfluss durch die Lampe immer noch ein wenig abnimmt, während der Glühfaden langsam seine Temperatur erreicht.&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-11-14_bulb_resistance.jpg" alt="Image: Measuring the resistance of an incandescent light bulb" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;h3 id="hintergrund"&gt;Hintergrund&lt;/h3&gt;&#10;&lt;p&gt;Der Glühfaden des Leutmittels wirkt wie ein Widerstand mit positivem Temperaturkoeffizienten (PTC): Bei Raumtemperatur hat er einen sehr niedrigen Widerstand, bei seiner normalen Betriebstemperatur von &lt;a href="https://hypertextbook.com/facts/1999/AlexanderEng.shtml" target="_blank" rel="noopener noreferrer" class="external-link"&gt;~3300K&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; einen viel höheren.&#10;Ein kleines Experiment beweist dies:&#10;Nehme ich eine &amp;ldquo;116W&amp;rdquo;-Halogenglühbirne, so beträgt ihr Widerstand im &amp;ldquo;eingeschwungenen Zustand&amp;rdquo; bei &lt;code&gt;230V&lt;/code&gt; etwa \(R_{steady} = \frac{U^2}{P} = 456{Ohm}\).&#10;Wenn ich den Widerstand der Lampe im kalten Zustand mit einem Multimeter messe, liegt der Wert bei \(R_{off} = 38.6{Ohm}\).&#10;Während durch die Glühlampe im eingeschalteten Zustand ein Strom von \(I_{steady} = 0.5A\) fließt, liegt ihr Einschaltstrom bei etwa \(I_{inrush} = 6A\) - also zwölf mal so hoch!&lt;/p&gt;&#10;&lt;h2 id="einschaltstrombegrenzer-schaltungen"&gt;Einschaltstrombegrenzer-Schaltungen&lt;/h2&gt;&#10;&lt;p&gt;Einschaltstrombegrenzer werden häufig in Geräten mit großen induktiven oder kapazitiven Lasten eingesetzt.&#10;&lt;a href="https://www.ti.com/lit/an/slvae30e/slvae30e.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Beispiele (Link zu Texas Instruments)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; sind Transformatoren, Motoren (induktiv), Leistungsverstärker, Schaltnetzteile (kapazitiv) usw.&lt;/p&gt;&#10;&lt;p&gt;Es gibt &lt;a href="https://e2e.ti.com/blogs_/b/powerhouse/posts/powertips-how-to-limit-inrush-current-in-an-ac-dc-power-supply" target="_blank" rel="noopener noreferrer" class="external-link"&gt;verschiedene Arten von Einschaltstrombegrenzern (TI application note)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; mit unterschiedlicher Komplexität.&lt;/p&gt;&#10;&lt;h3 id="widerstand-mit-negativem-temperaturkoeffizient-ntc--heißleiter---thermistor"&gt;Widerstand mit negativem Temperaturkoeffizient (NTC) / Heißleiter / Thermistor&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper left"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/ntc.jpg" alt="Image: Elektrisches Symbol eines Thermistors mit negativem Temperaturkoeffizienten" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ein Einschaltstrombegrenzer als NTC-Widerstand in Reihe zur Last ist die einfachste Schaltung in dieser Aufzählung. Sie besteht aus nur einem zusätzlichen Bauteil. Das Verhalten des NTC-Widerstands gegenüber der Temperatur ist dem des obigen Glühbirnenbeispiels entgegengesetzt: Sein Widerstand nimmt mit steigender Temperatur ab, so dass mehr Strom durch den Stromkreis fließen kann.&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-11-14_NTC.jpg" alt="Image: NTC curve example" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Bei den meisten NTCs verhält sich der Widerstand logarithmisch zur Temperatur, er sinkt also sehr stark bei zunehmender Temperatur. So kann er Einschaltstrompulse sehr effektiv unterdrücken. Nachteilig ist der Eigenenergiebedarf, der den Widerstand auf Betriebstemperatur hält.&#10;Diese Schaltung wird dort verwendet, wo geringe Kosten wichtig sind, das System einfach gehalten werden soll und der zusätzliche Energieverbrauch kein Problem darstellt.&lt;/p&gt;&#10;&lt;h3 id="widerstand-mit-bypass-relais"&gt;Widerstand mit Bypass-Relais&lt;/h3&gt;&#10;&lt;p&gt;Diese Schaltung vermeidet die Nachteile eines zusätzlichen Serienwiderstands zur Last durch Hinzufügen eines Leistungswiderstands und eines Relais. Sie wird typischerweise in Gleichstromversorgungen mit hoher Leistung oder empfindlichen Geräten wie Verstärkern verwendet.&#10;Der Relaiskontakt ist im unbestromten Zustand offen und parallel zum Leistungswiderstand verdrahtet, der je nach Last einen Wert von typischerweise &amp;ldquo;1&amp;hellip;22 Ohm&amp;rdquo; hat. Dahinter wird die Last angeschlossen, die den Einschaltstrom verursacht. Für dieses Beispiel nehme ich die Glühbirne eines Autos als Last. Beim Einschalten des Geräts begrenzt der Widerstand den Stromkreis und die Spannung auf der Lastseite steigt allmählich an. Sobald die Spannung an der Basis des NPN-&amp;ldquo;BC337&amp;rdquo;-Transistors hoch genug ist, schaltet er sich ein und lässt Strom durch die Relaisspule fließen, die dann den Leistungswiderstand kurzschließt, so dass die Last ihren Nennstrom ziehen kann.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-11-14_relay.jpg" alt="Image: Circuit plan for a start-up relay inrush current limiter" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die Werte von &lt;code&gt;R2, R3&lt;/code&gt; hängen von der Spannung hinter &lt;code&gt;R1&lt;/code&gt; ab, wenn sich die Schaltung bei ausgeschaltetem Relais im eingeschwungenen Zustand befindet. Des Weiteren ist die Charakteristik des Transistors und der Betriebsstrom der Relaisspule zu berücksichtigen. In meinem Beispiel wählte ich &lt;code&gt;R2 = 1kOhm&lt;/code&gt;, da der Transistor &lt;code&gt;~40mA&lt;/code&gt; für den Betrieb des Relais durchlassen muss. Diesen Wert habe ich dem Datenblatt des &lt;code&gt;BC337&lt;/code&gt;, Parameter Stromverstärkung, entnommen. Da ich das Relais nicht zu früh schalten lassen wollte, habe ich &lt;code&gt;R3&lt;/code&gt; hinzugefügt. Je höher sein Widerstand, desto früher wird das Relais aktiv. Ein zu geringer Widerstand hingegen reduziert die Basisspannung des &lt;code&gt;BC337&lt;/code&gt; so weit, dass er das Relais nicht mehr treiben kann - hier muss ein wenig experimentiert werden.&lt;/p&gt;&#10;&lt;p&gt;In diesem &lt;a href="https://www.elektronik-kompendium.de/public/schaerer/onilim.htm" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Anwendungsbeispiel für den &amp;ldquo;Soft Start&amp;rdquo; eines Ringtrafos (Elektronik-Kompendium)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; kommt eine ähnliche Schaltung zum Einsatz.&lt;/p&gt;&#10;&lt;p&gt;Und so verhält sich der Schaltkreis in der Realität:&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-11-14_relay_circuit.jpg" alt="Image: Start-up relay circuit on a breadboard" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich habe mir die Freiheit genommen, einen Schalter hinzuzufügen, damit die Steuerung der Stromversorgung für mich bequemer wird. Das Relais ist mit einer Freilaufdiode ausgestattet, um den Transistor vor negativen Spannungsspitzen zu schützen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-11-14_compare_inrush.jpg" alt="Image: Comparison of inrush currents with and without limiter" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Das Problem des Einschaltstroms ist tatsächlich gelöst. Der Vergleich (gelbe Linie) zeigt einen ungeregelten Einschaltstrom von &lt;code&gt;3.5A&lt;/code&gt;, während der Begrenzer ihn auf &lt;code&gt;0.8A&lt;/code&gt; reduziert, was nur knapp über dem Nennstrom liegt. Ein Leistungswiderstand mit kleinerem Wert hätte diesen Strom noch weiter reduziert.&lt;/p&gt;&#10;&lt;p&gt;Die grüne Linie zeigt die Basisspannung des &lt;code&gt;BC337&lt;/code&gt; an. Man erkennt, dass sie langsam ansteigt (die Genauigkeit des Oszilloskops ist bei &lt;code&gt;40VPP&lt;/code&gt; nicht die beste) und das Relais einschaltet, wenn sich der Stromfluss (gelbe Linie) stabilisiert hat.&lt;/p&gt;&#10;&lt;h3 id="sanftanlauf--konfigurierbare-strombegrenzung"&gt;Sanftanlauf / konfigurierbare Strombegrenzung&lt;/h3&gt;&#10;&lt;p&gt;Es gibt spezielle ICs, die den Sanftanlauf eines Systems unterstützen, je nach&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Leistungsbedarf&lt;/li&gt;&#10;&lt;li&gt;Hochlaufzeit&lt;/li&gt;&#10;&lt;li&gt;Beschränkungen der Anstiegsgeschwindigkeit&lt;/li&gt;&#10;&lt;li&gt;Maximalstrom&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Darüber hinaus gibt es verschiedene Arten von Einschaltstrombegrenzern, die je nach Anwendung ausgewählt werden können:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Zeitgesteuert: Das System wird nach einer bestimmten Zeit vollständig eingeschaltet (einfach, aber dumm)&lt;/li&gt;&#10;&lt;li&gt;Spannungsgesteuert: Das System wird oberhalb eines Spannungsauslösepegels vollständig eingeschaltet (immer noch einfach, mittlere Kosten)&lt;/li&gt;&#10;&lt;li&gt;Stromgesteuert: Begrenzt den Systemstrom oder schaltet das System auf der Grundlage eines Stromauslösepegels vollständig ein (wahrscheinlich teurer)&#10;Hinsichtlich der Anforderungen des Systems muss dann ein passender IC gewählt werden. Typische Namen sind hier &lt;code&gt;Power Supervisors&lt;/code&gt;, &lt;code&gt;IC soft starter&lt;/code&gt;, &lt;code&gt;Supervisor &amp;amp; Reset IC&lt;/code&gt; oder ähnlich. Sie können intern sogar mit Leistungstransistoren ausgestattet sein und selbsttätig schalten, wie z.B. &lt;a href="https://www.analog.com/en/products/MAX17562.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;MAX17562 (Analog Devices)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; für meine Evolution von &lt;a href="https://blog.schallbert.de/anywhereamps-get-pcb/#power-supervisor"&gt;AnywhereAmps&lt;/a&gt;.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Weiterlesen: &lt;a href="https://blog.schallbert.de/inrushlimiter-circuits/"&gt;Einschaltstrombegrenzer - Teil2&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>TVS-Dioden</title><link>https://blog.schallbert.de/knowledge-tvsdiodes/</link><pubDate>Sat, 13 Nov 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/knowledge-tvsdiodes/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-11-02_tvs-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Typical TVS voltage diagram"&#10; title="TVS-Dioden" /&gt;&#10;&lt;h2 id="elektronikwissen-zur-tvs-diode"&gt;Elektronikwissen zur TVS-Diode&lt;/h2&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/tvs_diode.jpg" alt="Image: Electric symbol for TVS diodes" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="was-ist-das"&gt;Was ist das?&lt;/h3&gt;&#10;&lt;p&gt;Eine Transient voltage suppressor diode schützt eine Schaltung vor Spannungsspitzen, die deutlich über der Nennspannung des Schaltkreises liegen. Sie sind für verschiedene Auslösespannungen und Ableitströme verfügbar. Zudem gibt es unterschiedliche Typen, wovon einige sogar sowohl vor positiven als auch vor negativen Spannungsspitzen schützen können.&lt;/p&gt;&#10;&lt;h3 id="unterschiede-zwischen-tvs--und-zenerdioden"&gt;Unterschiede zwischen TVS- und Zenerdioden&lt;/h3&gt;&#10;&lt;p&gt;Wie &lt;a href="https://en.wikipedia.org/wiki/Zener_diode" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Zener-Dioden&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, werden TVS-Dioden in Sperrrichtung eingebaut. Sie haben sogar &lt;a href="https://electronics.stackexchange.com/questions/233600/differences-between-tvs-diode-and-zener-diodes-in-diagrams-and-in-practice" target="_blank" rel="noopener noreferrer" class="external-link"&gt;dasselbe Symbol&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Doch währen Zenerdioden ihren Arbeitspunkt sehr nahe der Durchbruchspannung haben, werden TVS-Dioden üblicherweise weit darunter betrieben. Somit &lt;a href="https://toshiba.semicon-storage.com/content/dam/toshiba-ss-v2/master/en/semiconductor/knowledge/e-learning/discrete/discrete-basic-chap2_en.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;schließen sie Überspannungsspitzen kurz (Link zu Toshiba)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; auf für den Schaltkreis ungefährliche Spannungen und sind dafür ausgelegt, sehr hohe Pulsströme ableiten zu können.&lt;/p&gt;&#10;&lt;h3 id="abgrenzung-zwischen-tvs--and-schottkydioden"&gt;Abgrenzung zwischen TVS- and Schottkydioden&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/schottky_diode.jpg" alt="Image: Schottky diode electronics symbol" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Das Symbol für &lt;a href="https://en.wikipedia.org/wiki/Schottky_diode" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Schottkydioden&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; unterscheidet sich nur geringfügig von Zener/TVS-Dioden. Sie haben in der Regel eine niedrigere Durchlassspannung als ihre Siliziumpendants und sind für ihre schnelle Schaltgeschwindigkeit bekannt. Deshalb werden sie neben Gleichrichterschaltungen auch häufig in Verpolungsschutzschaltungen eingesetzt. TVS-Dioden haben das gleiche schnelle Schaltverhalten und die niedrige Vorwärtsspannung, aber im Gegensatz zu Schottkys haben sie einen genau definierten Schwellenwert für die Sperrspannung und vertragen besonders hohe Pulsströme.&lt;/p&gt;&#10;&lt;h3 id="auswahl-der-richtigen-tvs-diode"&gt;Auswahl der richtigen TVS-Diode&lt;/h3&gt;&#10;&lt;p&gt;Für Gleichstromanwendungen sollte eine unidirektionale TVS-Diode verwendet werden. Ihre Durchbruchspannung ist so zu wählen, dass sie etwas über der normalen Betriebsspannung liegt. Des Weiteren ist die Klemmspannung relevant, die weiter &amp;ldquo;unten&amp;rdquo; auf der Linie des zugehörigen Diagramms abzulesen ist. Liegt sie immer noch unter den &amp;ldquo;absoluten Maximalwerten&amp;rdquo; Ihrer zu schützenden Schaltung Schaltung? Wenn nicht ist eine andere TVS-Diode zu wählen oder ein Kompromiss bei der Spannung einzugehen (dabei nicht die Durchbruchspannung des TVS überschreiten). Alternativ kann auch eine andere Vorrichtung zur Unterdrückung von Spannungsspitzen wie einen spannungsabhängiger Widerstand oder ähnliches verwendet werden.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-11-02_tvs_diagram.jpg" alt="Bild: Diagramm einer typischen TVS-Diode" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Strom/Spannungs-Diagramm einer typischen TVS-Diode&lt;/span&gt;&lt;a href="https://www.littelfuse.com/products/tvs-diodes.aspx" class="attr-link" aria-label="Attribution 1"&gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;</description></item><item><title>Power supervisor MAX1756x bei kapazitiver Last</title><link>https://blog.schallbert.de/capacitor-problems/</link><pubDate>Tue, 02 Nov 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/capacitor-problems/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-11-02_ic-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: MAX17562 with polarized capacitors on in- and output. This is fatal."&#10; title="Power supervisor MAX1756x bei kapazitiver Last" /&gt;&#10;&lt;p&gt;Argh, ich habe Teile meiner &lt;a href="https://blog.schallbert.de/pcbs-arrived/"&gt;Power&amp;amp;Preamp Schaltung für AnywhereAmps Alpha&lt;/a&gt; hochgejagt. Beim Testen.&lt;/p&gt;&#10;&lt;h2 id="explosion"&gt;Vorfall&lt;/h2&gt;&#10;&lt;p&gt;Ich habe versehentlich &lt;code&gt;VBat&lt;/code&gt; und &lt;code&gt;GND&lt;/code&gt; auf meiner Platine vertauscht. Man sollte meinen, dass so etwas gar nicht passiert, doch ich muss nicht richtig aufgepasst haben. Der Kondensator für Entkopplung der Spannungseingangs explodierte sofort, aber mein Supervisor-Chip schützte den Rest der Schaltung.&lt;/p&gt;&#10;&lt;h2 id="reparatur-und-tests"&gt;Reparatur und Tests&lt;/h2&gt;&#10;&lt;p&gt;Nach der Reparatur mit einem Keramikkondensator habe ich einige Tests durchgeführt.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Verpolungstest - BESTANDEN&lt;/li&gt;&#10;&lt;li&gt;Harter Kurzschluss gegen Masse - BESTANDEN&lt;/li&gt;&#10;&lt;li&gt;Unterspannungsabschaltung - BESTANDEN&lt;/li&gt;&#10;&lt;li&gt;Wiederholung der normalen Spannung - &amp;hellip;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="unregelmäßigkeit"&gt;Unregelmäßigkeit&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-11-02_oszi.jpg" alt="Image: Oszilloskopbild eines erfolglosen Einschaltversuchs @18V, 5A Stromgrenze" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Als ich dann aber die Spannung langsam erhöhte, um die Unterspannungssperre zu umgehen, ließ sich das System nicht einschalten. Der Stromverbrauch stieg aber trotzdem an. Zur Analyse schloss ich ein Oszilloskop an, um zu sehen, was vor sich ging. Man kann sehen, dass die Kapazität der Endstufe (&lt;code&gt;&amp;gt;1400µF&lt;/code&gt;) wie ein weicher Kurzschluss gegen Masse wirkt, so dass die Versorgungsspannung innerhalb von &lt;code&gt;50µs&lt;/code&gt; um &lt;code&gt;2V&lt;/code&gt; auf den Auslösepegel der Unterspannungsabschaltung abfällt. Der Supervisor-IC schaltet dann die Last ab, so dass die Versorgungsspannung wieder ansteigt. Ich vermute, dass der Regler des Netzteils deswegen so komische Geräusche macht.&lt;/p&gt;&#10;&lt;p&gt;Nach Ablauf der Wartezeit nach Abschalten durch Fehler (Datenblatt MAX1756x) versucht er erneut, sich einzuschalten, und der ganze Vorgang beginnt von vorne.&lt;/p&gt;&#10;&lt;h2 id="inductance"&gt;Vorfall #2&lt;/h2&gt;&#10;&lt;p&gt;Als ich die Spannung an der &lt;code&gt;VCC&lt;/code&gt;-Schiene, die den Vorverstärker, die Endstufe und den 9-V-Spannungsregler versorgt, während des erfolglosen Einschaltens gemessen habe, war mir aufgefallen, dass negative Spannungsspitzen bei etwa &lt;code&gt;-4V&lt;/code&gt; auftreten, wenn das Supervisor-IC abschaltet. Dies wird durch die Induktivitäten in den Versorgungsleitungen der Endstufe verursacht.&#10;Während ich mich noch wunderte, dass diese ständigen Resets nicht gut sein könnten, explodierte mein Vorstufen-IC. Doof.&lt;/p&gt;&#10;&lt;h2 id="was-ich-gelernt-habe"&gt;Was ich gelernt habe&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Niemals einen gepolten Kondensator als Masse/Bypass &amp;ldquo;vor&amp;rdquo; den Schutzschaltungen verwenden!&lt;/li&gt;&#10;&lt;li&gt;Bypass-Kondensatoren größer als &lt;code&gt;100nF&lt;/code&gt; direkt an Spannungsdetektoren verwenden, wenn die geschalteten Lasten kapazitiv sind. Sie helfen, die Spannung lokal zu stabilisieren und wiederkehrende Neustarts zu vermeiden.&lt;/li&gt;&#10;&lt;li&gt;Zum Schutz der Ausgangsseite eines Leistungsschalters eine Schottky- oder TVS-Diode (Transient Voltage Suppressor) hinzufügen, die potenziell schädliche Spannungen abfangen kann.&lt;/li&gt;&#10;&lt;li&gt;Vorsichtshalber den Einschaltstrom (&lt;a href="https://blog.schallbert.de/knowledge-inrushlimiters/"&gt;Folgeartikel&lt;/a&gt;) begrenzen. Bei der nächsten Überarbeitung der Schaltung werde ich auf &lt;code&gt;MAX17563&lt;/code&gt; umsteigen. Er hat eine programmierbare Strombegrenzung, damit die Eingangsspannung gar nicht erst zu niedrig wird.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="schutz-vor-negativen-spannungsspitzen"&gt;Schutz vor negativen Spannungsspitzen&lt;/h3&gt;&#10;&lt;p&gt;Für meine Anwendung reicht eine schnell schaltende Klemmdiode in Schottky-Bauweise aus, da die Schaltung hinter dem MAX1756x-IC keine Überspannungsspitzen, sondern nur Rückwärtsspannungsspitzen (aufgrund der Induktivitäten im Schaltregler- und Verstärkerdesign) abbekommen wird. In &lt;a href="https://blog.schallbert.de/knowledge-tvsdiodes/"&gt;diesem Artikel&lt;/a&gt;, erkläre ich Transient Voltage Suppressors im Detail.&lt;/p&gt;&#10;</description></item><item><title>Videos mit dem VLC player konvertieren</title><link>https://blog.schallbert.de/video-conversion/</link><pubDate>Wed, 27 Oct 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/video-conversion/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-27_vlc-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: VLC media player icon"&#10; title="Videos mit dem VLC player konvertieren" /&gt;&#10;&lt;h2 id="website-medienerstellung"&gt;Website: Medienerstellung&lt;/h2&gt;&#10;&lt;p&gt;Für diese Website habe ich viele Mediendateien konvertiert. Die selbstgemachten Fotos sind sehr groß, was zu langen Ladezeiten der Seite und bildschirmfüllenden Bildern führt. Während es recht einfach ist, ihre Größe zu reduzieren und die JPEG-Komprimierung zu erhöhen, ist dies bei Videos schwieriger.&lt;/p&gt;&#10;&lt;h2 id="das-problem"&gt;Das Problem&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-27_wtf-vlc.jpg" alt="Image: VLCs Vorschau Effekte und Filter Fenster" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Hä, VLC?!&#10;Ich verwende den beliebten &lt;a href="https://www.videolan.org/vlc/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;vlc player&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; und irgendwie habe ich einige Grundkonzepte nicht richtig verstanden. Entweder wird mein Video nicht wie geplant in Graustufen angezeigt, die Ausgabedatei ist leer (Verwendung von &lt;code&gt;H.264&lt;/code&gt; statt &lt;code&gt;H.265&lt;/code&gt; behebt das Problem), das Seitenverhältnis scheint verzerrt zu sein, oder das Video hat sich plötzlich gedreht, was ich weder wollte noch erwartet habe.&lt;/p&gt;&#10;&lt;h2 id="in-diesem-beitrag"&gt;In diesem Beitrag&lt;/h2&gt;&#10;&lt;p&gt;Eine Reihe von Schritten hilft mir, wie man einfache Aufgaben zur Konvertierung und rudimentäre Bearbeitung von Videos mit vlc umsetzt.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/video-conversion/#colors"&gt;Farben verändern oder auf Graustufen wechseln&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/video-conversion/#rotation"&gt;Das Video rotieren&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/video-conversion/#cropping"&gt;Ausschnitt eines Videos verwenden, Auflösung oder Seitenverhältnis anpassen&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/video-conversion/#cutting"&gt;Ein Video schneiden&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://blog.schallbert.de/video-conversion/#scaling"&gt;Größe des Videos auf dem Datenträger verringern&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="wie-funktioniert-das-mit-den-filtern"&gt;Wie funktioniert das mit den Filtern?&lt;/h2&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-27_video-effects.jpg" alt="Image: VLC&amp;#39;s preview Effects and filters window" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="vorschau-der-filter"&gt;Vorschau der Filter&lt;/h3&gt;&#10;&lt;p&gt;VLC verfügt über eine Schnellvorschau für die gängigsten Filter im Menü &amp;ldquo;Werkzeuge -&amp;gt; Effekte und Filter&amp;rdquo;. Dies wirkt sich direkt auf das Video aus, das Sie ansehen, aber nicht auf Ihre gespeicherte Datei. Sie dient lediglich dazu, eine Vorschau zu erstellen und die Auswirkungen dieser Filter auf Ihr Video zu kontrollieren. In &lt;a href="https://www.videoproc.com/troubleshoot-vlc/how-to-crop-a-video-in-vlc.htm" target="_blank" rel="noopener noreferrer" class="external-link"&gt;diesem VideoProc-Blogeintrag&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; finden Sie ausführliche Anleitungen - und, was noch wichtiger ist, viele Bilder, die Sie dabei unterstützen. Für die ungeduldige Fraktion: weiterlesen 😉&lt;/p&gt;&#10;&lt;h3 id="gefiltertes-video-in-datei-speichern"&gt;Gefiltertes Video in Datei speichern&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-27_profile.jpg" alt="Image: VLC&amp;#39;s conversion profile window for saving" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Um einen Filter zu speichern, den Sie für Ihre Vorschau ausgewählt haben, müssen Sie ihn zum Konvertierungsprofil hinzufügen. Solange sie angekreuzt bleiben, wird &lt;em&gt;jedes&lt;/em&gt; Speichern mit diesem Profil Ihr Video verändern, auch wenn Sie alle Änderungen, die Sie in der Vergangenheit an den Vorschaufiltern vorgenommen haben, rückgängig gemacht haben!&lt;/p&gt;&#10;&lt;h2 id="colors"&gt;Wie kann ich die Farben in meinem Video ändern?&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Öffnen Sie die Videodatei, die Sie ändern möchten.&lt;/li&gt;&#10;&lt;li&gt;Klicken Sie auf &amp;ldquo;Werkzeuge -&amp;gt; Effekte und Filter&amp;rdquo; (oder drücken Sie &amp;ldquo;Strg+E&amp;rdquo;)&lt;/li&gt;&#10;&lt;li&gt;Es öffnet sich ein Dialogfeld mit drei Registerkarten. Wählen Sie die Registerkarte &amp;ldquo;Videoeffekte&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Markieren Sie unter &amp;ldquo;Essentials&amp;rdquo; die Option &amp;ldquo;Bildanpassung&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Nehmen Sie Ihre Änderungen vor und klicken Sie auf &amp;ldquo;Speichern&amp;rdquo; und dann auf &amp;ldquo;Schließen&amp;rdquo;. Eine Vorschau des Effekts wird im Hauptfenster angezeigt.&lt;/li&gt;&#10;&lt;li&gt;Öffnen Sie die Wiedergabelistenansicht durch Drücken von &amp;ldquo;Wiedergabeliste umschalten&amp;rdquo; (Strg+L).&lt;/li&gt;&#10;&lt;li&gt;Klicken Sie mit der rechten Maustaste auf Ihr Video und wählen Sie &amp;ldquo;Speichern&amp;hellip;&amp;rdquo; im Kontextmenü.&lt;/li&gt;&#10;&lt;li&gt;Drücken Sie das Schraubenschlüssel-Symbol &amp;ldquo;Ausgewähltes Profil bearbeiten&amp;rdquo; und wählen Sie auf der Registerkarte &amp;ldquo;Videocodec&amp;rdquo; die Unterregisterkarte &amp;ldquo;Filter&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Markieren Sie den Filter &amp;ldquo;Bildeigenschaften&amp;rdquo;. Dadurch wird Ihre Änderung durchgeführt, sobald Sie Ihre Datei speichern.&lt;/li&gt;&#10;&lt;li&gt;Geben Sie eine Zieldatei ein und klicken Sie auf &amp;ldquo;Start&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Vergessen Sie nicht, den Filter &amp;ldquo;Bildeigenschaften&amp;rdquo; in Ihrem Profil zu deaktivieren, wenn Sie damit fertig sind. Nehmen Sie auch die Änderungen zurück, die Sie auf der Registerkarte &amp;ldquo;Videoeffekte&amp;rdquo; vorgenommen haben (Schritt 4).&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="rotation"&gt;Wie drehe ich mein Video?&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Öffnen Sie die Videodatei, die Sie ändern möchten.&lt;/li&gt;&#10;&lt;li&gt;Klicken Sie auf &amp;ldquo;Werkzeuge -&amp;gt; Effekte und Filter&amp;rdquo; (oder drücken Sie &amp;ldquo;Strg+E&amp;rdquo;)&lt;/li&gt;&#10;&lt;li&gt;Es öffnet sich ein Dialogfeld mit drei Registerkarten. Wählen Sie die Registerkarte &amp;ldquo;Videoeffekte&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Wählen Sie unter &amp;ldquo;Geometrie&amp;rdquo; die Option &amp;ldquo;Transformieren&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Nehmen Sie Ihre Änderungen vor und klicken Sie auf &amp;ldquo;Speichern&amp;rdquo; und dann auf &amp;ldquo;Schließen&amp;rdquo;. Eine Vorschau des Effekts wird im Hauptfenster angezeigt.&lt;/li&gt;&#10;&lt;li&gt;Öffnen Sie die Wiedergabelistenansicht durch Drücken von &amp;ldquo;Wiedergabeliste umschalten&amp;rdquo; (Strg+L).&lt;/li&gt;&#10;&lt;li&gt;Klicken Sie mit der rechten Maustaste auf Ihr Video und wählen Sie &amp;ldquo;Speichern&amp;hellip;&amp;rdquo; im Kontextmenü.&lt;/li&gt;&#10;&lt;li&gt;Drücken Sie das Schraubenschlüssel-Symbol &amp;ldquo;Ausgewähltes Profil bearbeiten&amp;rdquo; und wählen Sie auf der Registerkarte &amp;ldquo;Videocodec&amp;rdquo; die Unterregisterkarte &amp;ldquo;Filter&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Markieren Sie den Filter &amp;ldquo;Videotransformation&amp;rdquo;. Dadurch wird Ihre Änderung durchgeführt, sobald Sie Ihre Datei speichern.&lt;/li&gt;&#10;&lt;li&gt;Geben Sie eine Zieldatei ein und klicken Sie auf &amp;ldquo;Start&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Vergessen Sie nicht, das Häkchen beim Filter &amp;ldquo;Bildeigenschaften&amp;rdquo; in Ihrem Profil zu entfernen. Nehmen Sie auch die Änderungen zurück, die Sie auf der Registerkarte &amp;ldquo;Videoeffekte&amp;rdquo; vorgenommen haben.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="cropping"&gt;Wie kann ich mein Video zuschneiden?&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Öffnen Sie die Videodatei, die Sie ändern möchten.&lt;/li&gt;&#10;&lt;li&gt;Klicken Sie auf &amp;ldquo;Werkzeuge -&amp;gt; Effekte und Filter&amp;rdquo; (oder drücken Sie &amp;ldquo;Strg+E&amp;rdquo;)&lt;/li&gt;&#10;&lt;li&gt;Es öffnet sich ein Dialogfeld mit drei Registerkarten. Wählen Sie die Registerkarte &amp;ldquo;Videoeffekte&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Spielen Sie unter &amp;ldquo;Zuschneiden&amp;rdquo; mit den Pixelwerten herum und beobachten Sie das Vorschaufenster genau, bis Sie mit dem Zuschnitt zufrieden sind. Notieren oder merken Sie sich die Einstellungen, die Sie anwenden möchten. Schließen Sie den Dialog.&lt;/li&gt;&#10;&lt;li&gt;Klicken Sie auf &amp;ldquo;Werkzeuge -&amp;gt; Voreinstellungen&amp;rdquo; (oder drücken Sie &amp;ldquo;Strg+P&amp;rdquo;)&lt;/li&gt;&#10;&lt;li&gt;Wählen Sie auf der Registerkarte &lt;code&gt;Interface&lt;/code&gt; (Start) die Optionsschaltfläche &lt;code&gt;Show Settings - All&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Scrollen Sie nach unten zum Element &amp;ldquo;Video&amp;rdquo; und öffnen Sie &amp;ldquo;Filter&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Wählen Sie den &amp;ldquo;Video-Zuschneidefilter&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Suchen Sie das Element &lt;code&gt;Cropadd&lt;/code&gt; in der Filterliste auf der linken Seite. Geben Sie die Werte ein, die Sie sich in der Vorschau gemerkt/aufgeschrieben haben. Klicken Sie auf &amp;ldquo;Speichern&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Öffnen Sie die Wiedergabelistenansicht, indem Sie auf &amp;ldquo;Wiedergabeliste umschalten&amp;rdquo; (Strg+L) drücken. Klicken Sie dann mit der rechten Maustaste auf Ihr Video und wählen Sie &amp;ldquo;Speichern&amp;hellip;&amp;rdquo; aus dem Kontextmenü.&lt;/li&gt;&#10;&lt;li&gt;Drücken Sie das Schraubenschlüssel-Symbol &amp;ldquo;Ausgewähltes Profil bearbeiten&amp;rdquo; und wählen Sie auf der Registerkarte &amp;ldquo;Videocodec&amp;rdquo; die Unterregisterkarte &amp;ldquo;Filter&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Aktivieren Sie den Filter &amp;ldquo;Videoschnitt&amp;rdquo;. Dadurch wird die Änderung durchgeführt, sobald Sie Ihre Datei speichern.&lt;/li&gt;&#10;&lt;li&gt;Geben Sie eine Zieldatei ein und klicken Sie auf &amp;ldquo;Start&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Vergessen Sie nicht, das Häkchen bei &amp;ldquo;Video-Zuschnittfilter&amp;rdquo; in Ihrem Profil zu entfernen. Nehmen Sie auch die Änderungen zurück, die Sie in den Einstellungen unter &amp;ldquo;Filter&amp;rdquo; vorgenommen haben.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="cutting"&gt;Wie kürze ich mein Video?&lt;/h2&gt;&#10;&lt;p&gt;Hinweis: Eine ausführlichere Version dieses Themas findet sich in &lt;a href="https://www.movavi.io/how-to-trim-video-in-vlc-en/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;diesem Beitrag von movavi&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Öffnen Sie die Videodatei, die Sie ändern möchten.&lt;/li&gt;&#10;&lt;li&gt;Zeigen Sie die Schaltfläche &amp;ldquo;Aufnehmen&amp;rdquo; im VLC Player an (markieren Sie &amp;ldquo;Ansicht-&amp;gt;Erweiterte Steuerelemente&amp;rdquo;).&lt;/li&gt;&#10;&lt;li&gt;Springen Sie an die Stelle, an der das geschnittene Video beginnen soll.&lt;/li&gt;&#10;&lt;li&gt;Drücken Sie die rote Schaltfläche &amp;ldquo;Aufnehmen&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Spielen Sie die Videosequenz ab. Halten Sie an der Stelle an, an der der Zuschnitt enden soll.&lt;/li&gt;&#10;&lt;li&gt;VLCs Standardspeicherort für die Videoausgabe: &lt;code&gt;C:\Benutzer\Benutzername\Video&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="scaling"&gt;Wie ändere ich die Videoauflösung?&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Öffnen Sie die Videodatei, die Sie ändern möchten.&lt;/li&gt;&#10;&lt;li&gt;Öffnen Sie die Wiedergabelistenansicht, indem Sie &amp;ldquo;Wiedergabeliste umschalten&amp;rdquo; (Strg+L) drücken. Klicken Sie dann mit der rechten Maustaste auf Ihr Video und wählen Sie &amp;ldquo;Speichern&amp;hellip;&amp;rdquo; aus dem Kontextmenü.&lt;/li&gt;&#10;&lt;li&gt;Drücken Sie das Schraubenschlüssel-Symbol &amp;ldquo;Ausgewähltes Profil bearbeiten&amp;rdquo; und wählen Sie auf der Registerkarte &amp;ldquo;Videocodec&amp;rdquo; die Unterregisterkarte &amp;ldquo;Auflösung&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Geben Sie die Videogröße ein, in die Sie konvertieren möchten, und klicken Sie auf &amp;ldquo;Speichern&amp;rdquo;.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;</description></item><item><title>Meine Platinen sind angekommen!</title><link>https://blog.schallbert.de/pcbs-arrived/</link><pubDate>Sat, 16 Oct 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/pcbs-arrived/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-16_board-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Freecad example design in 3D view"&#10; title="Meine Platinen sind angekommen!" /&gt;&#10;&lt;p&gt;Fortsetzung des Artikels &lt;a href="https://blog.schallbert.de/anywhereamps-get-pcb/"&gt;PCB Hardware Design&lt;/a&gt;&lt;/p&gt;&#10;&lt;h2 id="lieferung"&gt;Lieferung&lt;/h2&gt;&#10;&lt;p&gt;Nach weniger als zwei Wochen sind meine Platinen endlich angekommen. Zusammen mit allen Teilen, die zum Löten benötigt werden. Ich finde, sie sehen wunderschön aus 🙂&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-16_board-delivered.jpg" alt="Image: The PCB delivered as-is: green, pads tin-coated" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;h2 id="soldering"&gt;Löten: Schritt-für-Schritt&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-16_board-partial-test.jpg" alt="Image: cicruit testing" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich beschloss, die Platine in Abschnitten zu löten, beginnend mit dem Stromversorgungsbereich. Auf diese Weise kann ich die Abschnitte unabhängig voneinander testen und gegebenenfalls Fehler korrigieren. Der SOIC (Small Outline Integrated Circuit)-Umriss kann immer noch leicht von Hand gelötet werden, wenn man viel Flussmittel und eine Lupe für die optische Inspektion verwendet. Das einzige Problem war, dass der IC ein &amp;ldquo;exponiertes Pad&amp;rdquo; hat, d.h. es gibt eine Massefläche an der Unterseite des ICs zur besseren Wärmeableitung. Das zu löten war ein Albtraum. Schließlich gelang es mir, durch eine der unteren Durchkontaktierungen zu löten, während ich den Lötkolben fast eine Minute lang an einer anderen Durchkontaktierung hielt, bis es endlich klappte.&#10;Mit dem Ergebnis bin ich aber sehr zufrieden. Das System kann durch Ziehen von &lt;code&gt;*HVEN&lt;/code&gt; nach Masse aktiviert werden, wodurch die LED aufleuchtet. Die Tests auf Unterspannung/Kurzschluss verliefen sehr gut - der Akku ist jetzt geschützt.&lt;/p&gt;&#10;&lt;p&gt;Auch die Vorverstärkersektion war leicht zu löten. Nur die gepolten Kondensatoren erwiesen sich als so groß, dass sie einen großen Teil des Lötpads verdeckten - das ist ein Problem, weil das Lötzinn nicht sowohl Kondensator als auch Pad verbinden wollte, sondern nur an einem der beiden Kontakte hängen blieb. Ich half schließlich mit einem winzigen Stück Brückendraht nach.&lt;/p&gt;&#10;&lt;p&gt;Der 9V-Schaltregler-Teil - nun ja&amp;hellip; Ich habe es überhaupt nicht geschafft, ihn von Hand zu löten. Quadruple Flat Packs ohne Pins (QFN) sind schon schwer genug zu löten. Aber dieses Teil hier hat auch noch ein freiliegendes Pad auf der Unterseite und ich musste viel Massefläche vorhalten, um die Hitze von diesem Bauteil weg zu bekommen. Also konnte ich nicht einmal die Temperatur für den Lötvorgang hoch genug bekommen. Also habe ich mir eine Heißluftstation geliehen, und nach einigen ängstlichen Versuchen und dem Ertränken der Komponenten in Flussmittel habe ich es schließlich geschafft.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-16_board-inspection.jpg" alt="Image: optical PCB inspection" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Mit einem Makro-Objektiv auf meiner Kamera habe ich die Lötstellen dann geprüft.&lt;/p&gt;&#10;&lt;h2 id="funktionstest"&gt;Funktionstest&lt;/h2&gt;&#10;&lt;p&gt;Ich habe einen Tiefpass und einen Hochpass in das Design eingebaut, um 2-Wege-Lautsprechersysteme zu unterstützen. Um die drei Vorverstärkerausgänge (Fullrange, Tiefpass, Hochpass) zu testen, habe ich den Vorverstärker direkt an meinen Lautsprecher angeschlossen. Das ist keine gute Idee, denn die Impedanz des Lautsprechers ist viel niedriger als die des OP-Verstärkers, so dass die Ausgangsleistung sehr gering ist. Außerdem verbrauchen die unteren Frequenzen die meiste Leistung, so dass der Klang, den man hört, bereits ein &amp;ldquo;Hochpass&amp;rdquo; ist und die Lautstärke des Tiefpasses wesentlich geringer ist. Trotzdem funktioniert es, und ich kann die Verstärkung wie vorgesehen einstellen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&lt;video controls&gt;&#10; &lt;source src="https://blog.schallbert.de/assets/video/posts/2021-10-16_preamp-audio-demo.mp4" type="video/mp4"&gt;&#10; Your browser does not support the video tag.&#10; &lt;/video&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Preamp test&lt;/span&gt;&#10; &lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Das war alles, mein Design funktioniert! Da das alles quelloffen ist, steht es jedem frei zum Download &lt;a href="https://github.com/Schallbert/AnywhereAmps/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;auf meinem Github Repo&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zur Verfügung.&lt;/p&gt;&#10;&lt;h2 id="lektionen"&gt;Lektionen&lt;/h2&gt;&#10;&lt;p&gt;Hier sind die Designfehler, die in Revision2 korrigiert werden sollten:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Alle Komponenten in der gleichen Ausrichtung platziert werden (z.B. horizontal)&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;Thermische Entlastungen&amp;rdquo; (kurze Abschnitte, an denen die Leiterplan einen geringeren Querschnitt aufweist) sollten häufiger verwendet werden, um Lötprobleme aufgrund von zu starker Wärmeleitung zu vermeiden.&lt;/li&gt;&#10;&lt;li&gt;Freiliegende Pads: Beim Handlöten müssen die entsprechenden Lötpads zu den Seiten des ICs hin erweitert werden, um die Wärmeleitung des Lots zu verbessern&lt;/li&gt;&#10;&lt;li&gt;QFN-Fußabdrücke sind beim Handlöten falls möglich zu vermeiden. Sie sind lästig.&lt;/li&gt;&#10;&lt;li&gt;Wenn möglich, keine Bauteile mit &amp;ldquo;exposed pad&amp;rdquo; oder &amp;ldquo;power pad&amp;rdquo; verwenden. Eine Heißluftstation könnte ansonsten unumgänglich werden.&lt;/li&gt;&#10;&lt;li&gt;Schön viel Platz zwischen den Bauteilpads lassen, vor allem bei horizontaler Anordnung.&lt;/li&gt;&#10;&lt;li&gt;Da SMD-Kondensatoren mehr Höhe haben, sollten Sie größere Lötpads vorsehen.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Arbeitsabläufe mit FreeCAD</title><link>https://blog.schallbert.de/freecad-get-started/</link><pubDate>Tue, 12 Oct 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/freecad-get-started/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-12_pad-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Freecad example design in 3D view"&#10; title="Arbeitsabläufe mit FreeCAD" /&gt;&#10;&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://upload.wikimedia.org/wikipedia/commons/f/f7/FreeCAD-logo.svg" alt="FreeCad logo" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;FreeCad logo&lt;/span&gt;&lt;a href="https://commons.wikimedia.org/wiki/File:FreeCAD-logo.svg#file" class="attr-link" aria-label="Attribution 1"&gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich habe gerade eine zweite Bestellung für AnywhereAmps Alpha erhalten. Mein erster Prototyp hat bereits ein neues Zuhause, also werde ich mindestens drei weitere Exemplare bauen. Alle Holzarbeiten, die von Hand ausgeführt werden, würden 4-5 Stunden pro Exemplar in Anspruch nehmen, also habe ich mich entschlossen, sie auf den eigentlichen Bauprozess zu beschränken, der - hoffentlich - nur eine Stunde dauert.&#10;Dies möchte ich erreichen, indem ich die Holzteile mit einer CNC-Maschine fräsen lasse.&lt;/p&gt;&#10;&lt;h2 id="cad-werkzeug"&gt;CAD-Werkzeug&lt;/h2&gt;&#10;&lt;p&gt;Eine solche Maschine würde die Pläne, die ich von Hand skizziert habe, nicht verarbeiten. Also musste ich mich mit einer Software vertraut machen, die 2D-Skizzen in ein 3D-Modell umwandeln kann, und schließlich mit einem weiteren Werkzeug, das die Komponenten des 3D-Modells in eine Bahnsteuerung für die CNC-Maschine übersetzt, mit der ich meine Teile fräsen will.&#10;Für die 3D-Arbeiten habe ich die Software &lt;a href="https://www.freecad.org/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;FreeCAD&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; gewählt.&lt;/p&gt;&#10;&lt;h2 id="aller-anfang-ist-schwer"&gt;Aller Anfang ist schwer&lt;/h2&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-12_freecad_parts3d.jpg" alt="Image: parts construction complete" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Aber FreeCAD ist&amp;hellip; naja, ich bin halt kein CAD-Designer oder Maschinenbauingenieur. Daher ist der Einstieg in dieses Tool für mich ziemlich schwierig. Glücklicherweise gibt es einige gute Tutorials wie dieses: &lt;a href="https://www.youtube.com/watch?v=C1JG6l1uIJw" target="_blank" rel="noopener noreferrer" class="external-link"&gt;youtube link&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, dennoch habe ich einige Stunden gebraucht, bis ich mein erstes Teil gezeichnet hatte. Außerdem ist das, was auf dem Bild rechts zu erkennen ist, noch lange nicht der fertige Entwurf.&lt;/p&gt;&#10;&lt;h2 id="freecad-arbeitsablauf"&gt;FreeCAD Arbeitsablauf&lt;/h2&gt;&#10;&lt;p&gt;FreeCAD ist meiner Meinung nach (Stand Ende 2021) kein selbsterklärendes Werkzeug. Es ist sehr vielseitig und daher komplex und umfangreich. Da ich es zum Zeichnen von 3D-Teilen und/oder zur &lt;a href="https://de.wikipedia.org/wiki/Computerized_Numerical_Control#DIN/ISO-Programmierung_bzw._G-Code" target="_blank" rel="noopener noreferrer" class="external-link"&gt;G-Code&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;-Generierung verwenden möchte, werde ich nur eine Handvoll seiner Arbeitsbereiche benötigen.&lt;/p&gt;&#10;&lt;p&gt;Natürlich können die folgenden Zeilen nicht mehr als eine Schnellstartanleitung sein. Es gibt tonnenweise hilfreiches Material wie ein FreeCad &lt;a href="https://wiki.freecad.org/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Wiki&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, ein &lt;a href="https://forum.freecad.org/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Forum&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, und viele, viele Tutorial-Videos usw., die die Dinge besser und ausführlicher erklären als ich es kann. &lt;a href="https://wiki.freecad.org/Getting_started" target="_blank" rel="noopener noreferrer" class="external-link"&gt;https://wiki.freecad.org/Getting_started&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;h3 id="erst-die-abmessungen---das-modul-spreadsheet"&gt;Erst die Abmessungen - das Modul &amp;ldquo;Spreadsheet&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Schaffen wir zuerst einen zentralen Ort, an dem wir alle Maße unseres Projekts aufbewahren. Wir werden sie später in den Zeichnungen, 3D-Modellen und Baugruppen verknüpfen, so dass sich geänderte Werte in der Kalkulationstabelle automatisch auf alles andere angewendet werden (für mich übrigens &lt;em&gt;der&lt;/em&gt; Vorteil dieses CAD-Programms gegenüber vielen anderen, bei denen quasi von Vorn begonnen werden muss, wenn man die Größe des Bauteils anpassen möchte).&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Wählen Sie das Modul &amp;ldquo;Spreadsheet&amp;rdquo; an. Erstellen Sie ein Arbeitsblatt und benennen Sie es z. B. in &amp;ldquo;Abmessungen&amp;rdquo; um.&lt;/li&gt;&#10;&lt;li&gt;Tragen Sie Namen Ihrer Dimensionen ein. Ich habe festgestellt, dass es gut lesbar ist, wenn Sie für jede Dimension die Namenskonvention &lt;code&gt;Teil_Beschreibung_Attribut&lt;/code&gt; verwenden.&lt;/li&gt;&#10;&lt;li&gt;Nachdem Sie den Wert Ihrer Dimension eingegeben haben, klicken Sie mit der rechten Maustaste auf die Zelle und wählen Sie &amp;ldquo;Eigenschaften&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Im folgenden Dialog wählen Sie die &amp;ldquo;Alias&amp;rdquo;-Leiste und geben den Namen des Maßes &lt;code&gt;dimensionName&lt;/code&gt; ein.&lt;/li&gt;&#10;&lt;li&gt;Nun sind Sie in jeder Skizze in der Lage, diese Maße unter ihrem Alias auszuwählen. Klicken Sie &amp;ldquo;Save&amp;rdquo;, um den Dialog zu schließen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-12_spreadsheet.jpg" alt="Image: Spreadsheet example in FreeCad" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="skizze-anfertigen---scetcher-in-der-parts-design-workbench"&gt;Skizze anfertigen - scetcher in der &amp;ldquo;Parts Design Workbench&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Nun erstellen wir die erste Skizze des Teils, wobei die soeben erstellten Maße verwendet werden. Am besten beginnt man mit sogenannten Master-Skizzen, die alle Elemente einer Zeichnung in einer bestimmten Ansicht, z.B. auf der XY-Ebene, enthalten. Von ihnen werden dann die für dreidimensionale Modelle erforderlichen Grundriss-Skizzen der jeweiligen Achsenpaare abgeleitet.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Aktivieren Sie das Konstruktionsmodul &amp;ldquo;Part Design&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Erstellen Sie einen neuen Körper mit dem blauen Baustein-Symbol.&lt;/li&gt;&#10;&lt;li&gt;Erstellen Sie eine neue Skizze für diesen Körper mit dem Skizzen-Symbol. Wählen Sie z.B. die XY-Ebene.&lt;/li&gt;&#10;&lt;li&gt;Benennen Sie Ihre Skizze um, damit Sie sich ihren Zweck leicht merken können. Da Ihre ersten Skizzen die &amp;ldquo;Master&amp;rdquo;-Skizzen sind, schlage ich vor, sie &amp;ldquo;Master_IhrTeilename_xy&amp;rdquo; zu nennen.&lt;/li&gt;&#10;&lt;li&gt;Doppelklicken Sie auf die Skizze und beginnen Sie mit dem Zeichnen Ihrer Formen.&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-12_usingalias.jpg" alt="Image: Using spreadsheet alias in Parts Design workbench" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/li&gt;&#10;&lt;li&gt;Stellen Sie sicher, dass Sie Ihre Skizze vollständig definieren (&lt;a href="https://www.youtube.com/watch?v=F6H1iPwVg2o" target="_blank" rel="noopener noreferrer" class="external-link"&gt;youtube link&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;). Verwenden Sie die Abmessungen, die Sie in die Kalkulationstabelle eingegeben haben, als Hilfe: Doppelklicken Sie auf Ihre Maßeinschränkung und geben Sie &amp;ldquo;=&amp;rdquo; ein oder drücken Sie die kleine runde Schaltfläche &amp;ldquo;fx&amp;rdquo;. Geben Sie den Namen Ihrer Kalkulationstabelle ein (zum Glück gibt es hier die Autovervollständigung) und dann den Alias der Dimension, die Sie in der Definition widerspiegeln wollen.&lt;/li&gt;&#10;&lt;li&gt;Wiederholen Sie diesen Vorgang für jede Bemaßungsabhängigkeit Ihrer Zeichnung.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Mit Ihren fertigen Master-Skizzen (je eine für jede Achsenkombination) stehen alle Informationen für jeden Drittanbieter zur Verfügung, der Ihre Teile fertigen kann. Wenn Sie das tun möchten, können Sie Ihre Skizzen hier in DXF-Dateien exportieren, und schon sind Sie fertig.&#10;Andernfalls: Lesen Sie unten weiter.&lt;/p&gt;&#10;&lt;h3 id="3d-teile-erstellen---teile-design-workbench"&gt;3D-Teile erstellen - Teile Design Workbench&lt;/h3&gt;&#10;&lt;p&gt;Um ein echtes 3D-Teil aus Ihren Master-Skizzen zu erstellen, müssen Sie davon abgeleitete Skizzen erstellen, die extrudiert oder ausgeschnitten werden können. Sie müssen zunächst verstehen, wie FreeCAD Teile und Skizzen baumartig ausrichtet:&#10;Jedes extrudierte (3D)-Teil hat eine zugrunde liegende Skizze, die definiert, wie es aussieht. Seine Extrusionsrichtung hängt direkt von der Ebene der Skizze ab. Jedes Teil kann eine oder mehrere Bezugsebenen haben, an denen andere Teile befestigt werden können, die wiederum Teile, Löcher oder Taschen auf dieser spezifischen Fläche bilden.&lt;/p&gt;&#10;&lt;p&gt;FreeCAD zeigt nur das &amp;ldquo;letzte&amp;rdquo; Mitglied des Bauteilbaums an, das alle abhängigen Teile enthält, die Sie zuvor entworfen haben. Um die Sichtbarkeit umzuschalten, drücken Sie einfach die Leertaste.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-12_pad.jpg" alt="Image: FreeCAD&amp;#39;s pad design tree view" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Erstellen Sie eine neue Skizze in der Baumstruktur Ihres Teils. Dies wird Ihr erstes &amp;ldquo;Pad&amp;rdquo; sein, d.h. Ihr eigentliches 3D-Teil.&lt;/li&gt;&#10;&lt;li&gt;Wählen Sie das Werkzeug &lt;code&gt;create an edge linked to an external geometry&lt;/code&gt; (Hotkey &amp;ldquo;X&amp;rdquo;) und wählen Sie das Basisteil Ihrer ursprünglichen Master-Skizze aus.&lt;/li&gt;&#10;&lt;li&gt;Zeichnen Sie Ihre Form so, dass sie mit dieser Form übereinstimmt, und legen Sie ihre Maße fest, wiederum unter Verwendung der zuvor erstellten Bemaßungstabelle. Sie sollten dies jedoch nicht allzu oft benötigen, da Sie alle möglichen Kanten, die Sie finden konnten, bereits mit Ihrer Musterskizze verknüpft haben.&lt;/li&gt;&#10;&lt;li&gt;Jetzt kommt der spaßige Teil: Schließen Sie die Skizze, klicken Sie dann auf das Symbol &lt;code&gt;pad a selected sketch&lt;/code&gt; und geben Sie die gewünschte Höhe des Teils ein.&lt;/li&gt;&#10;&lt;li&gt;Das war&amp;rsquo;s!&lt;/li&gt;&#10;&lt;li&gt;Um ein weiteres Teil, eine Tasche oder ein Loch auf einer der Flächen Ihres Teiles zu erstellen, erstellen Sie zunächst eine neue Skizze und geben Sie ihr einen aussagekräftigen Namen. Klicken Sie dann auf die Fläche, auf der diese Skizze basieren soll. Wählen Sie schließlich das Werkzeug &lt;code&gt;set the Support of a scetch&lt;/code&gt; und wählen Sie die zuvor erstellte Skizze aus. Wiederholen Sie den Vorgang ab Schritt 2, bis Ihr Teil vollständig ist.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Hm, Ihr neues Teil sitzt direkt auf dem Master-Sketch, so dass Sie es nicht mehr sehen können? Keine Panik, lesen Sie einfach weiter.&lt;/p&gt;&#10;&lt;h3 id="3d-teile-anordnen---das-modul-draft"&gt;3D-Teile anordnen - das Modul &amp;ldquo;Draft&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Meiner Meinung nach ist es sehr mühsam, Teile in der Konstruktionswerkbank neu anzuordnen. Es wird viel einfacher, wenn Sie die Werkbank &amp;ldquo;Entwurf&amp;rdquo; (Draft) für diese Aufgabe verwenden. Auch hier können Sie Teile einfach klonen und nach Ihren Vorstellungen anordnen.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-12_move.jpg" alt="Image: move in FreeCAD&amp;#39;s Draft workbench" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Aktivieren Sie das Modul &amp;ldquo;Draft&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Wählen Sie das Pad/Teil, an dem Sie zuvor gearbeitet haben, im Teilebaum aus.&lt;/li&gt;&#10;&lt;li&gt;Klicken Sie auf das Schaf-Symbol, um Ihr Teil zu klonen.&lt;/li&gt;&#10;&lt;li&gt;Machen Sie das ursprüngliche Pad unsichtbar, indem Sie die Leertaste drücken, während das Teil aktiv ist.&lt;/li&gt;&#10;&lt;li&gt;Wählen Sie das geklonte Teil aus und benennen Sie es sinnvoll um, z.B. &lt;code&gt;derGerät_part&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Wählen Sie das &amp;ldquo;Verschieben&amp;rdquo;-Symbol (im obigen Bild grün markiert).&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-12_neat.jpg" alt="Image: part scetches and pads aligned side by side" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Eine Verschiebe-Operation besteht aus zwei Schritten: Zuerst müssen Sie die Referenzposition für Ihre Verschiebung eingeben. Entweder wählen Sie den Punkt aus, indem Sie irgendwo in der 3D-Ansicht klicken und dann den Cursor ein wenig bewegen (meine Empfehlung), oder Sie geben den Bezugspunkt in die Felder auf der linken Seite ein und bestätigen jede Achse mit &amp;ldquo;Enter&amp;rdquo;.&#10;Der zweite Schritt ist die eigentliche Bewegung gegenüber Ihrem Referenzpunkt. Ich empfehle, diese Werte mit der Tastatur einzugeben, da es unglaublich schwierig ist, mit der Maus eine exakte Position anzufahren.&#10;Bestätigen Sie mit &amp;ldquo;Enter&amp;rdquo;, und die Verschiebung sollte abgeschlossen sein.&lt;/p&gt;&#10;&lt;p&gt;Cool, jetzt haben Sie ein Bemaßungsblatt, Master-Skizzen, die Ihre Teile vollständig spezifizieren, die Teile selbst als 3D-Körper und das alles ordentlich sortiert in Ihrer Ansicht!&lt;/p&gt;&#10;&lt;h3 id="techdraw-erstellen-sie-eine-klassische-papier-2d-zeichnung-als-pdf"&gt;TechDraw: Erstellen Sie eine klassische &amp;ldquo;Papier 2D&amp;rdquo; Zeichnung als PDF&lt;/h3&gt;&#10;&lt;p&gt;Abschließend möchten Sie vielleicht eine klassische 2D-Zeichnung Ihres Teils exportieren? Zum Glück ist keine schwere Aufgabe.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Aktivieren Sie das &lt;code&gt;TechDraw&lt;/code&gt;-Modul.&lt;/li&gt;&#10;&lt;li&gt;Drücken Sie das Symbol &amp;ldquo;Standardseite einfügen&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Wählen Sie im Teilebaum das 3D-Teil aus, das Sie zeichnen lassen möchten.&lt;/li&gt;&#10;&lt;li&gt;Drücken Sie auf das Symbol &amp;ldquo;Mehrere Ansichten von zeichnungsfähigen Objekten einfügen&amp;rdquo; und wählen Sie die Ansichten aus, die vorhanden sein sollen.&lt;/li&gt;&#10;&lt;li&gt;Fügen Sie mithilfe der Symbolleiste Maße, Anmerkungen usw. hinzu.&lt;/li&gt;&#10;&lt;li&gt;Verwenden Sie die Funktion &amp;ldquo;Ansichtsrahmen ein-/ausschalten&amp;rdquo;, wenn sich Ihre Geometrien überschneiden, damit Sie sie wieder auswählen können.&lt;/li&gt;&#10;&lt;li&gt;Fügen Sie bei Bedarf kosmetische Linien ein, z. B. um Symmetrielinien zu zeichnen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Das resultierende Dokument könnte wie folgt aussehen:&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-12_drawing.jpg" alt="Image: classic drawing in FreeCAD" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;p&gt;Weitere Informationen zu diesem Modul sind auf dem &lt;a href="https://wiki.freecad.org/Basic_TechDraw_Tutorial" target="_blank" rel="noopener noreferrer" class="external-link"&gt;FreeCAD Wiki&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; verfügbar.&lt;/p&gt;&#10;</description></item><item><title>Tipps für besseres Hardware Design</title><link>https://blog.schallbert.de/anywhereamps-get-pcb/</link><pubDate>Tue, 21 Sep 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/anywhereamps-get-pcb/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-10-16_board-delivered-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Preamp prototype board soldered and working"&#10; title="Tipps für besseres Hardware Design" /&gt;&#10;&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;&#10;&lt;p&gt;Ich habe meine erste Anfrage für die Fertigung eines AnywhereAmps Alpha. Da ich nicht noch einmal 10h für den Bau aufwenden möchte, habe ich beschlossen, die Zeit in die Beschleunigung des Herstellungsprozesses zu investieren. Ich fand heraus, dass ich etwa eine Stunde pro Verstärker sparen könnte, wenn ich die Platine für mich herstellen lassen würde. Leider hat mich die Umstellung der Platine von manuellem Schneiden, Fräsen, Bohren und Löten auf ein PCB-Design doppelt so viel Zeit gekostet, als wenn ich sie bereits komplett gebaut hätte. Und ich weiß noch nicht einmal, ob das funktioniert. Aber fangen wir von vorne an.&lt;/p&gt;&#10;&lt;h2 id="anforderungen-an-die-neue-leiterplatte"&gt;Anforderungen an die neue Leiterplatte&lt;/h2&gt;&#10;&lt;p&gt;Die Platine soll enthalten&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;die Vorverstärkerschaltung, wie sie für AnywhereAmps Alpha entworfen wurde&lt;/li&gt;&#10;&lt;li&gt;die Zwei-Wege-Weichenschaltung, wie sie für &lt;em&gt;zukünftige&lt;/em&gt; AnywhereAmps Beta entwickelt wurde&lt;/li&gt;&#10;&lt;li&gt;einen schaltenden Abwärtswandler zur Bereitstellung von 9V DC für Aux-Effekte&lt;/li&gt;&#10;&lt;li&gt;eine Stromüberwachungsschaltung, die sowohl Batterie als auch Platine vor Überlastung, Kurzschluss, Unterspannung und Rückspannung schützt&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Auf diese Weise möchte ich die Elektronik schrittweise professionalisieren. Ich beabsichtige, die gleiche Platine für alle Varianten der AnywhereAmps zu verwenden. Außerdem würde ich einige externe Komponenten loswerden, die ich von Hand herstellen und platzieren musste: Sicherung, hochstromfähiger Netzschalter, 9V-Regler und seine Kühlung, sowie einige Kabel.&lt;/p&gt;&#10;&lt;h2 id="bauteilauswahl"&gt;Bauteilauswahl&lt;/h2&gt;&#10;&lt;h3 id="abwärtswandler"&gt;Abwärtswandler&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-09-21_switching_efficiency.jpg" alt="Image: Typische Wirkungsgradkurve eines Abwärtsschaltwandlers im Vergleich zum Laststrom" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Ich habe im Internet nach einem Abwärtsschaltwandler mit geringer elektromagnetischer Interferenz (EMI) und einem Ausgang von &lt;code&gt;&amp;gt;1A&lt;/code&gt; bei &lt;code&gt;9V&lt;/code&gt; gesucht. Ein Linearregler hätte zwar weniger Störungen ausgesendet, aber bei einer Batterie-Nennspannung von &lt;code&gt;18V&lt;/code&gt; hätte ich im schlimmsten Fall um &lt;code&gt;9W&lt;/code&gt; thermische Verluste in Kauf nehmen müssen. Daher wäre diese Lösung überhaupt nicht batterieschonend gewesen. Ich entschied mich für ein (teures) Design, das alle notwendigen Komponenten in einem Baustein enthält, so dass ich nur noch das PCB-Layout richtig hinbekommen musste, um die EMI zu reduzieren. Ich fügte ein paar Bypass-Kondensatoren und einen Spannungsteiler für den Zielspannungs-Sollwert hinzu. Das war&amp;rsquo;s! (Dachte ich damals noch. &lt;a href="https://blog.schallbert.de/pcbs-arrived/#soldering"&gt;So sah es tatsächlich aus&lt;/a&gt;)&lt;/p&gt;&#10;&lt;h3 id="power-supervisor"&gt;Power Supervisor&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-09-21_converter.jpg" alt="Image: Integrierter Abwärtswandler: Fast ein Würfel" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Der einzige Schutz für meine Verstärker war bisher eine Sicherung, deren Wert auf den höchsten Betriebsstrom eingestellt war, den ich in den Schaltkreisen erwartete. In den frühen Prototypenphasen habe ich einige Operationsverstärker durch versehentliche Verpolung zerstört. Außerdem möchte ich vermeiden, dass der Akku völlig entladen wird, wenn ich vergesse, auf die Ladeanzeigen zu achten. Deshalb habe ich einen &lt;a href="https://blog.schallbert.de/configure-max1756x/"&gt;Power Supervisor IC&lt;/a&gt; ausgewählt, der Über- und Unterspannung und weitere Fehler erkennt und sowohl die Schaltkreise als auch den Akku vor Schäden schützt.&lt;/p&gt;&#10;&lt;h3 id="gruppierung-passiver-bauteile"&gt;Gruppierung passiver Bauteile&lt;/h3&gt;&#10;&lt;p&gt;Ich habe gelernt, dass die Herstellung einer Leiterplatte mit vielen verschiedenen Bauteilen teurer und fehleranfälliger ist als bei Verwendung weniger unterschiedlicher Bauteile, auch wenn ihre Gesamtzahl auf diese Weise steigt. Also habe ich viele Werte überarbeitet und die Simulation für den Audio-Vorverstärker mit diesen neuen Werten erneut durchgeführt. Schließlich gelang es mir, die Schaltung von 8/5 auf 3 verschiedene Widerstands- und 3 verschiedene Kondensatorwerte zu reduzieren!&lt;/p&gt;&#10;&lt;h3 id="kondensatoren---mal-wieder"&gt;Kondensatoren - mal wieder&lt;/h3&gt;&#10;&lt;p&gt;Ich habe zunächst MLCC (Multilayer Ceramic Capacitors) in meinem Design verwendet, sowohl für den Bypass als auch für die Audio-Entkopplungskondensatoren. Nach erneuter Lektüre der Dokumente, auf die ich in meinem Beitrag &lt;a href="https://blog.schallbert.de/preamp-design-considerations/"&gt;preamp-design-considerations/&lt;/a&gt; hingewiesen hatte, stellte ich fest, dass dieses Kondensator-Design Probleme mit Verzerrungen bei Audiosignalen zu haben scheint. Das Bild unten zeigt einen Vergleich verschiedener Kondensatortypen und -größen. Man erkennt, dass Folienkondensatoren hier am besten und MLCCs am schlechtesten abschneiden. Da ich keine Folienkondensatoren in der von mir benötigten Größe auf SMD-Basis finden konnte und Aluminium-Elektrolytkondensatoren zu groß waren, habe ich gepolte Tantalkondensatoren gewählt. Für die Bypass-Kondensatoren wählte ich &lt;a href="https://passive-components.eu/why-low-esr-matters-in-capacitor-design/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;low-ESR&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; Typen.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-09-21_capcompare.jpg" alt="Image: capacitor types compared for Audio usage" /&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;TI Audio Design Journal: Vergleich verschiedener Kondensatortypen&lt;/span&gt;&lt;a href="https://www.ti.com/lit/an/slyt796/slyt796.pdf?ts=1630627717321&amp;amp;ref_url=https%253A%252F%252Fwww.startpage.com%252F" class="attr-link" aria-label="Attribution 1"&gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;h2 id="schaltungsentwurf"&gt;Schaltungsentwurf&lt;/h2&gt;&#10;&lt;p&gt;Ich wählte die Open-Source-Software &lt;a href="https://www.kicad.org/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;KiCad&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; als Werkzeug zum Schaltungsentwurf und zeichnete alle meine bestehenden Schaltpläne neu, wobei ich die oben beschriebenen Komponenten hinzufügte. Man erkennt gut, dass der Verstärkerteil nur die rechte Hälfte des Schaltplans füllt, während die linke Hälfte der Stromversorgung und der Überwachungsschaltung gewidmet ist.&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-09-21_schematics.jpg" alt="Image: AnywhereAmps Power &amp;amp; Preamp-Schaltung mit KiCad" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Ich habe versucht, die Schaltpläne so übersichtlich wie möglich zu gestalten, damit sie leicht zu lesen sind. Ich habe häufig globale Bezeichnungen verwendet und Steckverbinder zur einfacheren Montage hinzugefügt.&lt;/p&gt;&#10;&lt;h2 id="pcb-design"&gt;PCB-Design&lt;/h2&gt;&#10;&lt;p&gt;Dies war der schwierigste Teil für mich. Ich habe noch nie eine Leiterplatte für Audioanwendungen entworfen, also musste ich eine Menge Richtlinien lesen, bevor ich anfing.&lt;/p&gt;&#10;&lt;h3 id="anleitungen-und-tutorials"&gt;Anleitungen und Tutorials&lt;/h3&gt;&#10;&lt;p&gt;Ich liste einige davon unten zur Information auf:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://www.allaboutcircuits.com/technical-articles/clean-power-for-every-ic-part-1-understanding-bypass-capacitors/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Rauschen auf Leiterplatten und wie man es vermeidet&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.renesas.com/en/document/apn/an1325-choosing-and-using-bypass-capacitors" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Wie man Bypass-Kondensatoren am besten platziert&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.autodesk.com/products/eagle/blog/8-pcb-grounding-rules/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Massefläche oder nicht?&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://electronics.stackexchange.com/questions/52603/how-to-connect-ground-planes-together" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Wie man verschiedene Massen für Signal, Stromversorgung etc. verbindet&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://resources.altium.com/p/top-5-pcb-design-guidelines-every-pcb-designer-needs-know" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Bauteilpositionen für den Lötvorgang optimieren&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.elektronikpraxis.vogel.de/pcb-design-regeln-sieben-suenden-beim-leiterplatten-design-a-356703/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Übliche Probleme beim Platinendesign&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.multi-circuit-boards.eu/leiterplatten-design-hilfe/oberflaeche/leiterbahn-strombelastbarkeit.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Wie viel Strom kann ich über welche Leiterbahn schicken?&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://community.aisler.net/c/knowledge-base/design-rules/41" target="_blank" rel="noopener noreferrer" class="external-link"&gt;PCB Design Tipps für die maschinelle Fertigung&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="anywhereamps-pcb-erklärt"&gt;AnywhereAmps PCB erklärt&lt;/h3&gt;&#10;&lt;p&gt;Das Ergebnis nach ein einigen Stunden Arbeit:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-09-21_pcb.jpg" alt="Image: PCB fully routed on a 2-layer design" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Die Platine ist in zwei Lagen vollständig geroutet.&lt;/p&gt;&#10;&lt;p&gt;Ich habe die Leiterplatte in drei Bereiche unterteilt.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Links: der 9V-Schaltregler, der seinem Referenzdesign (Datenblatt) folgend mit Hilfe der Bypass-Kondensatoren einen Stromkreis um sich selbst bilden und den größten Teil der elektromagnetischen Störaussendung auslöschen würde. Das thermische Design, lässt viel Platz für Kupfer und einige Durchkontaktierungen, sodass die Wärme vom Chip gut abgeführt werden kann.&lt;/li&gt;&#10;&lt;li&gt;Mitte: Die Stromanschlüsse und die Überwachungsschaltung. Ich habe Lötbrücken gesetzt, um die Nennspannung der Batterie für die Unterspannungserkennung auszuwählen.&lt;/li&gt;&#10;&lt;li&gt;Rechts und oben: Vorverstärker und analoge Signalschaltung, getrennt von der übrigen Elektronik durch eine dicke Massebahn auf der oberen Ebene und eine eigene Massefläche auf der Unterseite. Ich hoffe, das ist genug.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Da ich die ersten Prototypen von Hand löten werde, um zu prüfen, ob alles gut funktioniert, habe ich &amp;ldquo;große&amp;rdquo; 0805 SMD-Bauteile verwendet. Ich habe den Abstand zwischen den Bauteilen größer gemacht, als es die PCB-Spezifikation verlangt. Leider waren die hochintegrierten Leistungs-ICs nicht in &amp;ldquo;Hobby-Löt-Elektronik&amp;rdquo;-Größen erhältlich, so dass ich hier noch einige Mühen auf mich nehmen muss.&lt;/p&gt;&#10;&lt;p&gt;So wird es also aussehen. Die Leiterplatte wird 45mm x 69mm groß sein.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-09-21_pcb3d.jpg" alt="Image: 3D-Ansicht der Leiterplatte" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;h2 id="herunterladen-oder-eigene-beiträge-erwünscht"&gt;Herunterladen oder eigene Beiträge erwünscht?&lt;/h2&gt;&#10;&lt;p&gt;Alle meine Zeichnungen in CAD sowie Stromlaufpläne sind quelloffen und zum Download auf meinem eigens dafür angelegten &lt;a href="https://github.com/Schallbert/AnywhereAmps/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Github Repo&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; verfügbar.&lt;/p&gt;&#10;&lt;p&gt;Nächste Schritte: &lt;a href="https://blog.schallbert.de/pcbs-arrived/"&gt;Platinen geliefert und verlötet&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>Vorverstärker-Design</title><link>https://blog.schallbert.de/preamp-design-considerations/</link><pubDate>Wed, 25 Aug 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/preamp-design-considerations/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/circuit_board_soldered-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Preamp prototype board soldered and working"&#10; title="Vorverstärker-Design" /&gt;&#10;&lt;h2 id="auswahl-der-entkopplungskondensatoren"&gt;Auswahl der Entkopplungskondensatoren&lt;/h2&gt;&#10;&lt;p&gt;Einige technische Hintergrundinformationen über die Auswahl der Komponenten für meine Vorverstärkerplatine.&lt;/p&gt;&#10;&lt;h3 id="warum-entkopplung-nötig-ist"&gt;Warum Entkopplung nötig ist&lt;/h3&gt;&#10;&lt;p&gt;Mit Entkopplung meine ich in diesem Fall speziell die Gleichstromentkopplung (DC-Entkopplung).&#10;Der Vorverstärker muss sowohl von der Quelle (dem Musikinstrument) als auch von der Senke (dem Endverstärker) entkoppelt werden.&#10;Dies hat mehrere Gründe:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;die Versorgungsspannungen der drei Partner können unterschiedlich sein (z.B. 9V Gitarrenspeisung, 18V Vorverstärker, 52V Endstufe) und ich möchte nicht versehentlich einen von ihnen kurzschließen&lt;/li&gt;&#10;&lt;li&gt;Ausgangsimpedanz, d.h. die Belastbarkeit der Ausgangsstufe könnte begrenzt sein. Wenn ich einen niedrigen Eingangswiderstand z.B. direkt an den Tonabnehmer einer Gitarre anschließe, kann es sein, dass ich überhaupt kein Signal herausbekomme, weil die Quellenspannung zu stark einbricht.&lt;/li&gt;&#10;&lt;li&gt;Rauschunterdrückung durch elektrische Entkopplung&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="warum-kondensatoren-die-aufgabe-übernehmen-können"&gt;Warum Kondensatoren die Aufgabe übernehmen können&lt;/h3&gt;&#10;&lt;p&gt;Es gibt mehrere Möglichkeiten, Schaltungen elektrisch zu entkoppeln. Man kann einen &lt;a href="https://en.wikipedia.org/wiki/Opto-isolator" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Optokoppler&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; verwenden, um zwei Schaltkreise vollständig (d.h. galvanisch) voneinander zu trennen. Damit wird nur das erwünschte Signal zwischen den Schaltkreisen durchgelassen. Oder man verwendet - wie in meinem Fall - einen Kondensator, um nur Wechselströme durchzulassen und Gleichstrom zu unterdrücken.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Wie funktioniert das?&lt;/em&gt;, fragen Sie?&#10;Nun, ein Kondensator funktioniert ein bisschen wie ein Wassertank. Wenn man eine konstante Spannung (Druck) anlegt, füllt sich der Tank bis zu einem gewissen Grad. Wenn dieser Pegel erreicht ist, fließt kein Volumenstrom mehr, weder rein noch raus. Variiert man nun aber die Spannung um einen statischen Pegel (Wasserdruckschwankung), so wird man wieder einen Volumenstrom messen, der in den Tank hinein- und herausfließt.&lt;/p&gt;&#10;&lt;p&gt;Genauso verhält es sich mit dem Kondensator. Wenn man ihn in Reihe mit einer Senke schaltet, wird er nur dann Strom durchlassen, wenn sich der die Eingangsspannung ändert.&lt;/p&gt;&#10;&lt;h2 id="typ-des-entkopplungskondensators"&gt;Typ des Entkopplungskondensators&lt;/h2&gt;&#10;&lt;p&gt;Keramik-Vielschichtkondensator, Polypropylen-Kondensator, Elektrolytkondensator&amp;hellip;? Nach Lektüre &lt;a href="https://www.ti.com/lit/an/slyt796/slyt796.pdf?ts=1630627717321&amp;amp;ref_url=https%253A%252F%252Fwww.startpage.com%252F" target="_blank" rel="noopener noreferrer" class="external-link"&gt;dieses Leitfadens&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; von Texas Instruments habe ich verstanden, dass es in diesem Dokument nur um Verzerrungen geht. Die Verzerrung würde am stärksten am Instrumenteneingang vor dem Vorverstärker Einfluss ausüben, da dieser von beiden Verstärkerstufen verstärkt wird. Bei der Verwendung eines Elektrolytkondensators sollte eine Vorspannung angelegt werden, um die Verzerrung im unteren Frequenzbereich zu verringern. Bei meinem Design ist die Vorspannung &lt;code&gt;1/2&lt;/code&gt; von &lt;code&gt;VCC&lt;/code&gt;, also noch höher als die in der Anleitung verwendeten &lt;code&gt;5V&lt;/code&gt; - ich denke, ich werde mit einem &amp;ldquo;billigen&amp;rdquo; polarisierten Aluminiumkondensator auskommen um die Verzerrungen &lt;a href="https://en.wikipedia.org/wiki/Total_harmonic_distortion" target="_blank" rel="noopener noreferrer" class="external-link"&gt;THD+n&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; gering zu halten.&lt;/p&gt;&#10;&lt;p&gt;Es gibt noch eine andere Sache, die ich bei Mehrschicht-Keramikkondensatoren (MLCCs) festgestellt habe: Wenn die Lautsprecher bei hoher Vorverstärkung nicht angeschlossen sind, spielen die von mir verwendeten Endstufen immer noch Musik! Wie kommt das? Die Endstufenplatine verwendet MLCCs, die wie Piezoaktoren wirken. Ihre Vibration wird durch die Platine, auf der sie montiert sind, verstärkt, was einen gewissen Schallpegel ergibt. Verrückt. Weitere interessante Details finden Sie im &lt;a href="https://www.edn.com/power-tip-49-avoid-these-common-mlcc-pitfalls/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;EDN-Blog&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; oder - in einem weiteren &lt;a href="https://e2e.ti.com/blogs_/b/powerhouse/posts/how-to-reduce-acoustic-noise-of-mlccs-in-power-applications" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Texas Instruments-Leitfaden&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 id="kondensatorgröße"&gt;Kondensatorgröße&lt;/h3&gt;&#10;&lt;p&gt;Um die Größe des Kondensators zu beurteilen sind einige Aspekte zu berücksichtigen:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Ein größerer Kondensator senkt meine untere Grenzfrequenz&lt;/li&gt;&#10;&lt;li&gt;Ein größerer Kondensator belastet die Eingangsquelle aufgrund von Ladungsverlusten stärker&lt;/li&gt;&#10;&lt;li&gt;Ein größerer Kondensator kostet mehr, insbesondere die hochwertigen Kondensatoren.&lt;/li&gt;&#10;&lt;li&gt;Ein größerer Kondensator kann einen größeren Platzbedarf haben.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Ich wollte eine untere Grenzfrequenz von &lt;code&gt;&amp;lt;20Hz&lt;/code&gt; für volle HiFi-Fähigkeit. Da dies bereits der &lt;code&gt;-3dB&lt;/code&gt;-Punkt ist, wählte ich einen noch niedrigeren Wert von &lt;code&gt;15Hz&lt;/code&gt;, berechnet über \(f=\frac{1}{2 \pi RC}\). Bei einer Eingangsimpedanz von &lt;code&gt;10kOhm&lt;/code&gt; (der Wert des Widerstandes, der an die virtuelle Masse geklemmt wird) ergebe ich eine Zielgröße des Eingangskondensators von &lt;code&gt;1µF&lt;/code&gt;.&#10;Bei diesem Kondensator wird die Eingangsquelle wesentlich stärker durch den Eingangswiderstand von &lt;code&gt;10kOhm&lt;/code&gt; beeinflusst als durch den Kondensator mit seinem niedrigen Serienwiderstand (&lt;a href="https://passive-components.eu/why-low-esr-matters-in-capacitor-design/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;ESR&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;) von einigen hundert &lt;code&gt;mOhm&lt;/code&gt;, so dass der gewählte Wert in Ordnung ist.&lt;/p&gt;&#10;&lt;p&gt;Für den Ausgangskondensator ist die Berechnung ähnlich. Die Endstufe hat eine Eingangsimpedanz von &lt;code&gt;20kOhm&lt;/code&gt;, so dass die untere Grenzfrequenz von &lt;code&gt;17Hz&lt;/code&gt; bereits mit einem &lt;code&gt;0.47uF&lt;/code&gt; Kondensator erreicht wird. Um die Wertunterschiede in Grenzen zu halten, habe ich mich auch hier für den &lt;code&gt;1µF&lt;/code&gt; entschieden.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/projects/aa_alpha/"&gt;Zurück zur Projekt-Hauptseite&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>Shelly13 - Teil2</title><link>https://blog.schallbert.de/thoughts-about-shelly/</link><pubDate>Fri, 09 Jul 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/thoughts-about-shelly/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-07-09_shellydraw-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: shelly13&amp;#39;&amp;#39;s folding mechanism"&#10; title="Shelly13 - Teil2" /&gt;&#10;&lt;h2 id="shelly13-projektstatus-prototypenskizzen"&gt;Shelly13: Projektstatus, Prototypenskizzen&lt;/h2&gt;&#10;&lt;p&gt;Ich denke immer noch viel über &lt;a href="https://blog.schallbert.de/shelly13/"&gt;Shelly13&lt;/a&gt; nach und darüber, wie ein größerer Lautsprecher, ein homogener Klang mit etwas Punch und ein versiegeltes Gehäuse, das sich zusammenklappen lässt, umsetzbar wäre.&lt;/p&gt;&#10;&lt;h3 id="gehäuse-dimensionierung-und-volumen"&gt;Gehäuse: Dimensionierung und Volumen&lt;/h3&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/shelly13/scetch_unfolded.jpg" alt="Image: Cabinet, inside view scetch" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt; Die letzten Entwürfe in meinem Kopf benötigen 12x10 und 14x10 Zoll Kessel. Der Combo wird aufgeklappt ein bisschen wie eine Torte aussehen, mit einem Gesamtvolumen von ca. 38l inkl. Lautsprecher und Verstärker im Inneren. Die Gesamthöhe wird mit ausgeklappten Füßen ca. 75cm betragen, zusammengeklappt ist er ca. 29cm hoch.&lt;/p&gt;&#10;&lt;h3 id="lautsprecher"&gt;Lautsprecher&lt;/h3&gt;&#10;&lt;p&gt;Während mein erster Entwurf einen 10&amp;quot;-Lautsprecher an der Vorderseite des Gehäuses vorsieht, werde ich höchstwahrscheinlich zu einem Zwei-Wege-Design wechseln, um die Montage zu vereinfachen und die Probleme mit dem Fräsen einer großen gewölbten Fläche und der Herstellung einer Adapterplatte zu verringern. Ich werde also einen 8&amp;quot;-Basslautsprecher in einem Downfire-Setup und einen 3&amp;quot;-Mittelhochtonlautsprecher an der Front wählen.&lt;/p&gt;&#10;&lt;h3 id="verstärkung"&gt;Verstärkung&lt;/h3&gt;&#10;&lt;p&gt;Ich denke darüber nach, einen Car-Hifi-Verstärker zu verwenden, der sein Signal von einer aktiven Frequenzweiche mit getrennten Ausgängen für tiefe und hohe Frequenzen erhält, wodurch ein Bi-Amp-Design mit gebrückten Anschlüssen entsteht.&lt;/p&gt;&#10;&lt;h3 id="klappmechanismus"&gt;Klappmechanismus&lt;/h3&gt;&#10;&lt;p&gt;Das Zusammenklappen erfolgt mit Hilfe von Teleskopschienen, die zwischen den Kesseln montiert werden. Es ist wichtig, dass das Gehäuse im aufgeklappten Zustand luftdicht verschlossen ist. Deshalb muss ein Dichtungsring vorhanden sein, der einrastet, wenn der untere Holzring des 12&amp;quot;-Kessels auf den oberen Ring des 14&amp;quot;-Kessels trifft. &lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/shelly13/scetch_top_rings.jpg" alt="Image: Cabinet ring system scetch" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;h2 id="nein-das-ist-zu-komplex"&gt;Nein, das ist zu komplex!&lt;/h2&gt;&#10;&lt;p&gt;Ok, vereinfachen wir das Konzept. Lassen wir die Faltbarkeit für den ersten Prototyp weg. Reduzieren wir stattdessen die Gehäusegröße. Konzentrieren wir uns zunächst auf die Elektronik und die Herstellbarkeit.&#10;Das Ergebnis dieser Umfangsänderung ist: &lt;a href="https://blog.schallbert.de/projects/aa_alpha/"&gt;AnywhereAmps Alpha&lt;/a&gt;!&lt;/p&gt;&#10;</description></item><item><title>Shelly13 - Teil1</title><link>https://blog.schallbert.de/shelly13/</link><pubDate>Mon, 28 Jun 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/shelly13/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/shelly13/shelly13-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: shelly13 prototype in CAD"&#10; title="Shelly13 - Teil1" /&gt;&#10;&lt;p&gt;Nachdem ich eine Weile mit &lt;a href="https://blog.schallbert.de/projects/mobfobamp/"&gt;MobFobAmp&lt;/a&gt; gespielt hatte, wollte ich einen weiteren Schritt in der Entwicklung zu einer wirklich guten tragbaren Lösung gehen. Mit diesem Design versuche ich, die Unzulänglichkeiten der vorherigen Prototypen zu beheben:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Die Ausgangsleistung könnte höher sein&lt;/li&gt;&#10;&lt;li&gt;Mehr Volumen in den tiefen Tönen wäre schön&lt;/li&gt;&#10;&lt;li&gt;Der Klappmechanismus ist ein bisschen fummelig.&lt;/li&gt;&#10;&lt;li&gt;Ich hätte gerne eine Entfaltung in einem Schritt, am besten mit einer Hand möglich&lt;/li&gt;&#10;&lt;li&gt;Mechanische Stabilität und Robustheit könnten verbessert werden&lt;/li&gt;&#10;&lt;li&gt;Empfindliche Bauteile an der Außenseite, die mit Vorsicht zu behandeln sind&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="ein-erster-entwurf"&gt;Ein erster Entwurf&lt;/h2&gt;&#10;&lt;p&gt;Unter Berücksichtigung der oben genannten Punkte habe ich herausgefunden, dass ich bei der Faltbarkeit einige Kompromisse eingehen muss, um alle empfindlichen Komponenten einschließlich der Lautsprecherchassis in das Innere des Gehäuses zu bringen. Ein kugelförmiges Design wäre ideal für ein optimales Verhältnis zwischen Abmessungen und Volumen, aber ich wüsste nicht, wie man ein solches Design herstellt. Also entschied ich mich für einen zylindrischen Ansatz.&#10;Ich möchte, dass der Lautsprecher &amp;ldquo;aufklappt&amp;rdquo;, damit er beim Zusammenklappen vor mechanischen Schäden geschützt ist.&#10;Meine erste Idee sieht also so aus:&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/shelly13/shelly13.jpg" alt="Image: Shelly13 scetch" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;h2 id="einschränkungen"&gt;Einschränkungen&lt;/h2&gt;&#10;&lt;p&gt;Ich kann die Zylinder nicht selbst herstellen, also müsste ich einen Schlagzeughersteller bitten, mir ein paar unfertige Tom-Toms in Sondergröße zu schicken. Selbst wenn ich diese bekomme, muss ich auf einer gekrümmten Oberfläche schneiden, was ich noch nie gemacht habe, und ich müsste etwas 3D-Drucken / Fräsen, um eine Adapterplatte zwischen Lautsprecher und Zylinder herzustellen&amp;hellip;&#10;Oder werde ich den Lautsprecher einfach oben/unten anbringen?&#10;Außerdem müsste ich mir überlegen, wie ich einen leistungsfähigeren Verstärker in mein Design einbauen kann - es gibt einige Ideen wie &lt;a href="https://pioneer-car.eu/gm-d1004/gm-d1004" target="_blank" rel="noopener noreferrer" class="external-link"&gt;diesen Pioneer-Auto-Verstärker&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; oder den spezialisierten Hi-Fi-Verstärker von &lt;a href="https://www.hypexdirect.com/products/modules/amplifier-modules/ucd180hg-with-hxr" target="_blank" rel="noopener noreferrer" class="external-link"&gt;DIY Class D&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, aber die haben sehr unterschiedliche und möglicherweise hohe Anforderungen an die Stromversorgung&amp;hellip;&lt;/p&gt;&#10;&lt;h2 id="fazit"&gt;Fazit&lt;/h2&gt;&#10;&lt;p&gt;Es gibt also einige offene Baustellen. Ich lasse die Punkte mal auf mich wirken und schaue dann, wie ich mit diesen Problemen am besten umgehe.&lt;/p&gt;&#10;</description></item><item><title>Favicon und Sammlungsgenerator</title><link>https://blog.schallbert.de/jekyll-icon-paginator/</link><pubDate>Sun, 13 Jun 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/jekyll-icon-paginator/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/favicon-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: favicon implementation on a website"&#10; title="Favicon und Sammlungsgenerator" /&gt;&#10;&lt;h3 id="hinzufügen-eines-tab-symbols---favicon"&gt;Hinzufügen eines &amp;ldquo;Tab&amp;rdquo;-Symbols - Favicon&lt;/h3&gt;&#10;&lt;p&gt;Ich war besorgt über eine merkwürdige Fehlermeldung, die Jekyll bei jedem Refresh meiner Seite ausgab: &lt;code&gt;[2021-06-13 22:07:43] ERROR '/favicon.ico' not found.&lt;/code&gt; Einige Online-Recherchen ergaben, dass dies weder ein Jekyll- noch ein Theme-Fehler ist, sondern ein Dateifehler. Siehe &lt;a href="https://blog.schallbert.de/projects/thissite/#favicon"&gt;hier&lt;/a&gt; wie ich das Icon für diese Seite implementiert habe.&lt;/p&gt;&#10;&lt;h3 id="erzeugung-von-kollektionen"&gt;Erzeugung von Kollektionen&lt;/h3&gt;&#10;&lt;p&gt;Ich habe herausgefunden, dass irgendwie die Sammlungserzeugung auf meiner Seite nicht funktioniert. Trotzdem geht das Layouting-Skript aber davon aus, dass es die Seiten vorbereitet hat. Wenn ich nun versuche, die Seite durch Drücken der Schaltfläche &amp;ldquo;Zurück&amp;rdquo; aufzurufen, erhalte ich einen 404-Fehler. Bis jetzt war ich nicht in der Lage, dies zu beheben, weil die Erzeugung stillschweigend fehlschlägt und ich nicht weiß, wo.&#10;Ich schalte sie vorerst komplett ab.&lt;/p&gt;&#10;</description></item><item><title>MobFobAmp</title><link>https://blog.schallbert.de/mobfobamp-diy/</link><pubDate>Thu, 10 Jun 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/mobfobamp-diy/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/unfolded-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: MobFobAmp Folded"&#10; title="MobFobAmp" /&gt;&#10;&lt;h2 id="plans"&gt;Pläne&lt;/h2&gt;&#10;&lt;section class="hugo-gallery"&gt;&#10; &lt;div class="hugo-gallery__frame"&gt;&#10; &lt;div class="hugo-gallery__grid" role="list"&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-0" aria-label="Image: MobFobAmp pencil drawing on a sheet of paper. It features front and side view, and includes a scetch of the speaker chassis which is mounted inside-out. The box itself looks like a rectangle with hinges that allow it to fold flat."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/scetch_sidefront.jpg" alt="Image: MobFobAmp pencil drawing on a sheet of paper. It features front and side view, and includes a scetch of the speaker chassis which is mounted inside-out. The box itself looks like a rectangle with hinges that allow it to fold flat." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-1" aria-label="Image: MobFobAmp pencil drawing on a sheet of paper. It is a top/bottom view, and includes domensions. It looks like a square ."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/scetch_topbottom.jpg" alt="Image: MobFobAmp pencil drawing on a sheet of paper. It is a top/bottom view, and includes domensions. It looks like a square ." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;a class="hugo-gallery__thumb" href="#gallery-full-2" aria-label="Image: MobFobAmp&amp;#39;s circuit diagram as a pencil drawing. It shows a battery, fuse, on/off switch, status indicator LED the amplifier board with in- and output jacks and the speaker it drives."&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/scetch_circuitdiagram.jpg" alt="Image: MobFobAmp&amp;#39;s circuit diagram as a pencil drawing. It shows a battery, fuse, on/off switch, status indicator LED the amplifier board with in- and output jacks and the speaker it drives." class="hugo-gallery__thumb-img" loading="lazy" /&gt;&#10; &lt;/a&gt;&lt;/div&gt;&lt;div class="hugo-gallery__caption"&gt;Plans for my Mobile Foldable Amplifier.&lt;/div&gt;&lt;/div&gt;&#10;&lt;/section&gt;&#10;&#10; &lt;div class="hugo-gallery__full" aria-live="polite"&gt;&lt;figure id="gallery-full-0" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/scetch_sidefront.jpg" alt="Image: MobFobAmp pencil drawing on a sheet of paper. It features front and side view, and includes a scetch of the speaker chassis which is mounted inside-out. The box itself looks like a rectangle with hinges that allow it to fold flat." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Front and side view scetch with dimensions&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-1" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/scetch_topbottom.jpg" alt="Image: MobFobAmp pencil drawing on a sheet of paper. It is a top/bottom view, and includes domensions. It looks like a square ." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Top and bottom view scetch with dimensions, shows the mounting side of the panels&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure id="gallery-full-2" class="hugo-gallery__figure"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/scetch_circuitdiagram.jpg" alt="Image: MobFobAmp&amp;#39;s circuit diagram as a pencil drawing. It shows a battery, fuse, on/off switch, status indicator LED the amplifier board with in- and output jacks and the speaker it drives." class="hugo-gallery__full-img" /&gt;&lt;figcaption class="hugo-gallery__figcaption"&gt;Minimal added circuitry: Switch, fuse, LED, 1k Resistor to set amp gain to max&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;/section&gt;&#10;&lt;h2 id="listen"&gt;Listen&lt;/h2&gt;&#10;&lt;h3 id="werkzeuge"&gt;Werkzeuge&lt;/h3&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Benötigte Werkzeuge&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Bohrmaschine&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Oberfräse, Kopierring, Parallelanschlag&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Multitool: Schleifen, Holzschnitt, Schneidkopf, Zirkel&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Optional: Handkreissäge mit Schutzbügel&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Handsäge, fein&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Zwingen&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Stechbeitel, 12mm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Hammer&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schraubendreher, Bits&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Holzbohrer: 3mm, 5mm, 6mm, 8mm, 12mm, Senkbohrer 45°, Forstner 30mm&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Stahlbohrer: 3mm, Senkbohrung 45°&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Messerköpfe: 8mm gerade, 12mm gerade, 45° optional&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lötkolben, Lötzinn, Schrumpfschlauch, 1m 1x0,75mm² Leitung&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="materialliste"&gt;Materialliste&lt;/h3&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Artikel&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Größe [mm]&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Anzahl&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;~ Kosten [€]&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Vorder-/Rückseite: Birke Multiplex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;L 500 B 260 B 12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Oben/Unten: Birke Multiplex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;L 260 B 248 B 12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Seiten: Birke Multiplex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;L 500 B 110 B 12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;8&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Verstärkung &amp;amp; Halterungen&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;L 260 B 236 W 12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schrauben (vorzugsweise Torx)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12 x 3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;100&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schrauben (Batteriehalter, Lenker)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;35 x 4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,20&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schrauben &amp;amp; Muttern (Lautsprecher)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;15 x M4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0,20&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Platinenhalter&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;d5 x 16&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;14&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Klavierband&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;500 x 32&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;18&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Verschlüsse&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;50 x 12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;9&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Fahrradschlauch (Flexible Scharniere &amp;amp; Riemen)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;28&amp;quot;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&amp;ldquo;P-Typ&amp;rdquo; Schaumstoff- oder Gummidichtung&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;500 x 9 x 7&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;8&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;P-Typ&amp;quot; Schaumstoff- oder Gummidichtung&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;260 x 9 x 7&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Schaumstoff- oder Gummidichtung vom Typ &amp;ldquo;P&amp;rdquo;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;236 x 9 x 7&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;I-Typ&amp;quot; Schaumstoffdichtung (Lautsprecher)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;600 x 9 x 2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Aluminiumschiene&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;260 x 12&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Abstandhalter (Auto-unfold Funktion)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;do 30 di 4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Tragegriff&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Was Sie bevorzugen&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;?&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Verstärker (z.B. Adafruit 20W Class-D Verstärker)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;20&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Lautsprecher (z.B. Visaton W 170S 4Ohm)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;d170, W 81&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;35&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Batterie, 10V - 14V&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Was Sie bevorzugen&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;?&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Sicherung, Schalter, LED, Widerstände: Siehe Schaltplan&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Was Sie bevorzugen&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;?&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Ersatzholz z.B. als Maske&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;z.B. 300 x 300&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Gummikleber oder Heißkleber (luftdichte Lücken)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;20g&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="der-bau"&gt;Der Bau&lt;/h2&gt;&#10;&lt;h3 id="verlegung"&gt;Verlegung&lt;/h3&gt;&#10;&lt;p&gt;Zuerst werden alle Falze gefräst damit die Dichtungen in das Gehäuse montiert werden können.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/scetch_routerripfence_7mm.png" alt="Image: router_ripfence_7mm" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Montieren Sie den geraden &lt;code&gt;8mm&lt;/code&gt;-Fräser. Bei einer Schnitttiefe von &lt;code&gt;3mm&lt;/code&gt; setzen Sie den Parallelanschlag so ein, dass die Mitte der Fräse &lt;code&gt;7mm&lt;/code&gt; vom Anschlag entfernt ist. Achten Sie darauf, dass Sie die Oberfräse gut führen, damit Ihre Schnitte gerade und sauber werden.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Fahren Sie mit den vorderen und hinteren Platten fort (500x260x12). Fräsen Sie die langen Außenseiten der beiden Bretter.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Schneiden Sie nun die klappbaren Seiten des Schrankes zu. Nehmen Sie dafür zwei der vier Teile &amp;ldquo;500x110x12&amp;rdquo;. Die langen Seiten müssen nach oben zeigen. Verwenden Sie Ihre Oberfräse, um die Innenseiten dieser Bretter wie folgt zu schneiden:&#10;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/scetch_bevelsides_8mm.png" alt="Image: bevel_sides_8mm" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Nun ist es an der Zeit, die umlaufenden Dichtungen von Deckel und Boden zu fertigen. Nehmen Sie die &lt;a href="https://blog.schallbert.de/mobfobamp-diy/#plans"&gt;Pläne&lt;/a&gt; zur Hilfe.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Montieren Sie den 12mm-Fräser auf die Oberfräse. Stellen Sie den Parallelanschlag neu ein, damit die Oberfräse ohne Abstand zum Anschlag fräst. Fräsen Sie einen Falz von etwa 4mm Tiefe an eine Seite von Deckel und Boden, damit hier später das Gummischarnier montiert werden kann.&#10;Schleifen Sie überstehende Fasern vorsichtig mit einem 180er-Papier ab.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/rubberhinge_cutout.jpg" alt="Image: cutout for the rubber hinge" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Verwenden Sie einen an der Oberfräse angebrachten Zirkel oder das Multitool, um den Innenring mit einem Durchmesser von &lt;code&gt;148mm&lt;/code&gt; für die Lautsprecheröffnung zu schneiden. Drei Durchgänge auf der Oberfräse sollten genug sein.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Fertigen Sie eine Schablone an. Nehmen Sie dafür ein Reststück, z.B. aus Sperrholz. Der Lochkreis der Schablone sollte wie folgt berechnet werden: Außendurchmesser des Lautsprechers plus Differenz aus Abstand Kopierring zu Achsmitte und Fräserradius. So erreichen Sie einen Enddurchmesser der Falz von &lt;code&gt;170mm&lt;/code&gt;. Es sollte jetzt so aussehen:&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/speakercutout_mask.jpg" alt="Image: mask for the speaker inlay" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Zentrieren Sie die Schablone auf dem &lt;code&gt;148mm&lt;/code&gt; Ausschnitt an der Vorderseite und befestigen Sie sie (z.B. mit doppelseitigem Klebeband). Montieren Sie den Kopierring an der Oberfräse. Stellen Sie sicher, dass Sie den &lt;code&gt;12mm&lt;/code&gt; Fräskopf verwenden und eine Fasen-Tiefe von nicht mehr als &lt;code&gt;8mm&lt;/code&gt; verwenden - der Lautsprecher hat &lt;code&gt;7mm&lt;/code&gt; Tiefe, fügen Sie &lt;code&gt;1mm&lt;/code&gt; für die Dichtung hinzu.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Schleifen Sie die Kanten ein wenig ab. Verwenden Sie das Multitool, um kleine Unebenheiten zu korrigieren, die eventuell noch vorhanden sind. Wiederholen Sie den Vorgang, bis der Lautsprecher gut sitzt.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/speakercutout_ready.jpg" alt="Image: Speaker cutout plus inlay complete" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Schneiden Sie nun die Aussparungen für die Gegenstücke der Verriegelungen. Stellen Sie die Oberfräse auf die Tiefe des Materials + Tiefe der Dichtungsnut ein (damit sie hinter der Dichtung sitzen kann) und verwenden Sie den Parallelanschlag, um die Mitte der oberen und unteren Teile anzupassen. Beachten Sie, dass Sie orthogonal durch die Nuten für die Dichtungen schneiden, die Sie zuvor hergestellt haben.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/latch_counterpart.jpg" alt="Image: latch" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="beschläge"&gt;Beschläge&lt;/h3&gt;&#10;&lt;p&gt;Die Beschläge werden vor dem Zusammenbau des Gehäuses montiert, da der Gegendruck der Dichtungen das Schließen erschwert.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Montieren Sie die Haken an der oberen und unteren Platte mit je zwei Schrauben &lt;code&gt;3x12&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Verwenden Sie Heiß- oder Gummikleber, um die Metallkanten gegen den Ausschnitt abzudichten.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/latch_counterpart_sealed.jpg" alt="Image: latch_counterpart_sealed" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/li&gt;&#10;&lt;li&gt;Schneiden Sie ein Stück aus dem Fahrradschlauch, Länge: &lt;code&gt;260mm&lt;/code&gt; .&lt;/li&gt;&#10;&lt;li&gt;Schneiden Sie dieses Stück parallel zur langen Seite in zwei Hälften, so dass zwei Riemen entstehen.&lt;/li&gt;&#10;&lt;li&gt;Bohren Sie 5 Löcher in gleichem Abstand in jede Aluminiumstange und benutzen Sie die &lt;code&gt;45°&lt;/code&gt; Senker um später die Schraubenköpfe dort einpassen zu können.&lt;/li&gt;&#10;&lt;li&gt;Legen Sie einen Gummiriemen unter die Alustange und befestigen Sie ihn an der oberen Rückseite der Deckelplatte.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/rubberhinge_mounted.jpg" alt="Image: rubberhinge_mounted" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/li&gt;&#10;&lt;li&gt;Wiederholen Sie Schritt 6 mit der Bodenplatte.&lt;/li&gt;&#10;&lt;li&gt;Legen Sie das vordere, obere und untere Brett zusammen auf den Tisch, so dass sie ein großes C bilden.&lt;/li&gt;&#10;&lt;li&gt;Bringen Sie die Verschlüsse mittig am vorderen Brett an und stellen Sie sicher, dass sie die oberen/unteren &amp;ldquo;Deckel&amp;rdquo; fest geschlossen halten und die oberen/unteren Bretter in einem 90°-Winkel zur Vorderseite stehen.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="positionierstücke"&gt;Positionierstücke&lt;/h3&gt;&#10;&lt;p&gt;Montieren Sie nun die Fixierungselemente, die eigentlich Regalböden an ihrem Platz halten, an der Ober- und Unterseite des Gehäuses. Sie fixieren die Seitenteile an ihrem Platz und versteifen das Gehäuse.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Markieren Sie die Stellen, an denen die Regalbodenhalter am Rand der Nut angebracht werden sollen. Die Abstände finden Sie &lt;a href="https://blog.schallbert.de/mobfobamp-diy/#plans"&gt;in den Plänen&lt;/a&gt;.&lt;/li&gt;&#10;&lt;li&gt;Verwenden Sie einen &lt;code&gt;5mm&lt;/code&gt; Holzbohrer und markieren mit Klebeband eine Tiefe von &lt;code&gt;8mm&lt;/code&gt;, damit nicht ganz durch das Brett gebohrt wird.&lt;/li&gt;&#10;&lt;li&gt;Bohren Sie die 6 Löcher in Deckel- und Bodenplatte und stecken dann die Positionierstücke dort hinein.&lt;/li&gt;&#10;&lt;li&gt;Stellen Sie sicher, dass die Kragen der Positionsstücke auf gleicher Höhe sind wie die Oberfläche der Platte (und nicht in der Nut versenkt sind).&lt;/li&gt;&#10;&lt;li&gt;Entfernen Sie vorerst die Fixierungen wieder, denn die Dichtungen müssen vorher montiert werden.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="dichtungen"&gt;Dichtungen&lt;/h3&gt;&#10;&lt;p&gt;Sie machen das Gehäuse schließlich luftdicht, werden aber bei Klappvorgängen recht stark beansprucht.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Achten Sie darauf, dass die gefrästen Vertiefungen sauber und trocken sind, damit das Klebeband die volle Haltekraft entfalten kann.&lt;/li&gt;&#10;&lt;li&gt;Beginnen Sie mit je 4 Dichtungen für Boden- und Deckelplatte. Sie könnten etwas breiter sein als die Vertiefung, verwenden Sie in diesem Fall eine Plastikkarte oder ein ähnliches, weiches Werkzeug, um sie einzupassen.&lt;/li&gt;&#10;&lt;li&gt;Nehmen Sie ein Rasiermesser oder ein Messer mit einer spitzen Klinge zur Hand um Löcher in die Dichtungen zu schneiden, in die die Fixierstücke hineingedrückt werden können Orientieren Sie sich dabei an den Markierungen für die Löcher, die Sie zuvor angebracht haben.&lt;/li&gt;&#10;&lt;li&gt;Benutzen Sie einen Hammer, um die Positionierungselemente vorsichtig durch die Dichtungen zu drücken.&lt;/li&gt;&#10;&lt;li&gt;Nehmen Sie zum Schluss einen kleinen Schraubenzieher oder eine Klinge, um die Dichtung wie einen kurzen Kragen um die Fixierungen herum anzuheben - das Gehäuse muss luftdicht verschlossen werden.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/gaskets_mounted.jpg" alt="Image: gaskets_topbottom_mounted" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/li&gt;&#10;&lt;li&gt;Bringen Sie die Dichtungen an den beiden Seitenteilen an.&lt;/li&gt;&#10;&lt;li&gt;Bringen Sie zwei Dichtungen pro Vor/Rückwand an.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="automatisches-auffalten"&gt;Automatisches Auffalten&lt;/h3&gt;&#10;&lt;p&gt;Dies ist eine optionale Funktion. Mit ihrer Hilfe klappt sich das Gehäuse von selbst auf, sodass das Fixieren mittels Boden- und Deckelplatte leichter fällt.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Nehmen Sie die vier Seitenteile zur Hand. Bohren Sie mit einem Forstnerbohrer einen &lt;code&gt;3mm&lt;/code&gt; tiefen Kreis genau in die Mitte der Innenseiten der Seitenteile.&lt;/li&gt;&#10;&lt;li&gt;Schneiden Sie zwei &lt;code&gt;70mm&lt;/code&gt; lange Stücke aus dem Gummischlauch.&lt;/li&gt;&#10;&lt;li&gt;Befestigen Sie die erhaltenen Riemen an zwei zusammengehörigen Seitenteilen mit Hilfe der &lt;code&gt;30mm&lt;/code&gt; Unterlegscheibe und einer Schraube genau in dem Kreis, der vorher ausgeschnitten wurde.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/autounfold.jpg" alt="Image: auto unfold feature" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Die Unterlegscheiben werden benötigt, um den Bereich zu vergrößern, in dem das Gummiband am Holz befestigt ist. Ohne sie würde das Gummi um die Schraube herum reißen.&lt;/p&gt;&#10;&lt;h3 id="scharniere"&gt;Scharniere&lt;/h3&gt;&#10;&lt;p&gt;Die Montage der Scharniere mit den Dichtungen ist etwas fummelig. Sie könnten ein weiteres Paar Hände oder einige Klammern benötigen.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Legen Sie die Frontplatte auf den Tisch. Setzen Sie die beiden Seitenteile mit den Dichtungen so auf der Frontplatte auf, dass sie auf der Dichtung &amp;ldquo;stehen&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/scetch_pianohinge_corner.png" alt="Bild: Montage der Scharniere an der inneren Ecke" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Während die Seitenteile gegen die Dichtungen der Frontplatte gedrückt werden (orange), setzen Sie die Scharniere (rot) ein und markieren die Positionen der Löcher.&lt;/li&gt;&#10;&lt;li&gt;Montieren Sie die Scharniere. Achten Sie darauf, dass kein Spalt zwischen den Seiten und der Frontplatte entsteht.&lt;/li&gt;&#10;&lt;li&gt;Wiederholen Sie den Vorgang mit den anderen beiden Seiten und der Rückwand.&lt;/li&gt;&#10;&lt;li&gt;Setzen Sie die beiden Hälften zusammen. Sie werden sich aufgrund des Drucks der Dichtung nach innen bewegen wollen, verwenden Sie also die Verstärkungsplatte, um das Gehäuse vollständig aufgeklappt zu halten. Ich habe für diesen Schritt ein Stück Holz mit den Maßen &lt;code&gt;236 x 27 x 12&lt;/code&gt; verwendet.&lt;/li&gt;&#10;&lt;li&gt;Wenn Sie die Auto-Unfold-Funktion vorbereitet haben, ist es jetzt an der Zeit, die losen Enden des Gummibandes auf Spannung zu bringen und sie auf der gegenüberliegenden Seite zu befestigen.&lt;/li&gt;&#10;&lt;li&gt;Montieren Sie schließlich die beiden Scharniere auf beiden Seiten der Schrankaußenfläche. Achten Sie darauf, dass kein Spalt zwischen den Seitenteilen entsteht.&lt;/li&gt;&#10;&lt;li&gt;Probieren Sie den Mechanismus aus. Wenn Sie das Gehäuse zusammenklappen, sollte er sich von selbst entfalten, wenn Sie die Scharniere richtig montiert haben.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="oben-und-unten"&gt;Oben und unten&lt;/h3&gt;&#10;&lt;p&gt;In diesem Schritt werden die Fixierlöcher gebohrt und das Gummischarnier montiert. Auf diese Weise werden nur zwei statt acht Verriegelungen benötigt und der mechanische Druck auf die Dichtungen bleibt hoch genug.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Halten Sie das Gehäuse aufgeklappt.&lt;/li&gt;&#10;&lt;li&gt;Tragen Sie etwas Farbe auf die bereits in Deckel- und Bodenplatte montierten Fixierungen auf.&lt;/li&gt;&#10;&lt;li&gt;markieren Sie die Stellen zum Bohren der Löcher auf der Ober- und Unterseite des Gehäuses, indem Sie diese ausrichten und dann die aufgeklappte Gehäuseschale und die Ober- und Unterteile zusammenpressen.&lt;/li&gt;&#10;&lt;li&gt;&lt;div class="media-wrapper right"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/scetch_rubberhinge_rear.png" alt="rubber_hinge" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Benutzen Sie den &lt;code&gt;5mm&lt;/code&gt; Bohrer, um die Löcher an den Stirnseiten des Gehäuses entsprechend der markierten Punkte zu bohren, Tiefe wiederum &lt;code&gt;8mm&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Legen Sie Boden- und Deckelplatte auf und prüfen, ob alles gut passt. Probieren Sie die Verschlüsse aus. Wenn die Riegel nicht 100%ig passen, kann man mit der Bohrmaschine vorsichtig nachhelfen.&lt;/li&gt;&#10;&lt;li&gt;Befestigen Sie die obere und untere Platte mit den beiden verbleibenden Aluminiumstangen (grün) und dem losen Ende des Gummischarniers (schwarz) an der Rückseite des Gehäuses, wie in der Skizze gezeigt.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="funktioniert-alles"&gt;Funktioniert alles?&lt;/h3&gt;&#10;&lt;p&gt;Wow, der Verstärker ist fast fertig! Das Zusammen- und Auseinanderklappen sollte jetzt funktionieren.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/cabinet_complete.jpg" alt="montierter Schrank" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Montieren Sie den Lautsprecher, aber vergessen Sie nicht, zuerst die Dichtung einzubauen.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/speaker_assembly.jpg" alt="speaker gaskets" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;Drei Maschinenschrauben &lt;code&gt;M4x15&lt;/code&gt; mit Muttern sorgen für eine sichere Befestigung des Lautsprechers.&lt;/li&gt;&#10;&lt;li&gt;Um den Verstärker zu konfigurieren sind ein paar Brücken zu verlöten. Befolgen Sie dafür die &lt;a href="https://learn.adafruit.com/adafruit-20w-stereo-audio-amplifier-class-d-max9744/analog-control" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Anleitung von Adafruit&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; für eine analoge Steuerung der Verstärkung. Montieren Sie das Potentiometer oder stellen den Verstärker gleich auf 100% Verstärkung, dem &lt;a href="https://blog.schallbert.de/mobfobamp-diy/#plans"&gt;Schaltplan&lt;/a&gt; folgend.&lt;/li&gt;&#10;&lt;li&gt;Sicherung und Schalter werden eventuell noch nicht benötigt. Anstelle einer Batterie kann ein Netzteil mit Stromregelung angeschlossen werden, zum Schutz vor Kurzschlüssen auf &lt;code&gt;12V @ 1A&lt;/code&gt; einstellen.&lt;/li&gt;&#10;&lt;li&gt;Für einen Funktionstest löten Sie eine &lt;code&gt;6,3mm&lt;/code&gt;-Eingangsbuchse an ein paar Drähte oder ein altes Instrumentenkabel und stecken es in die Klemmen des linken Kanals des Verstärkers.&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/electronics_assembly.jpg" alt="Image: Elektronik anschließen" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/li&gt;&#10;&lt;li&gt;Schließen Sie Ihr Instrument an und probieren den Verstärker aus.&lt;/li&gt;&#10;&lt;li&gt;Hinweis: Wenn Ihr Instrument passiv ist, d.h. keinen eingebauten Vorverstärker hat, benötigen Sie höchstwahrscheinlich einen zusätzlichen Vorverstärker oder eine aktive DI-Box, um eine akzeptable Ausgangslautstärke zu erzielen. &lt;a href="https://www.tech21nyc.com/products/sansamp-2/bassdriver-di/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Z.B. dieses Ding&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; wird die Aufgabe gut erfüllen. Die meisten Effektpedale haben aber ebenfalls Vorverstärker eingebaut - ich habe eines dafür verwendet.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="fertigstellung-des-projekts"&gt;Fertigstellung des Projekts&lt;/h3&gt;&#10;&lt;p&gt;Schauen Sie sich an, wie schlank, leicht und einfach zu transportieren Ihr Verstärker ist, wenn er zusammengefaltet ist!&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/mobfobamp/folded_standing.jpg" alt="folded MobFobAmp, standing" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&lt;/p&gt;&#10;&lt;p&gt;Einige zusätzliche Schritte, die ich unternommen habe, waren, dem Verstärker ein angemessenes Gehäuse zu geben (das leere Schraubengehäuse&amp;hellip;), Sicherung, Eingangsbuchse(n), Schalter, LED und die Batterie hinzuzufügen. Wird der Akku eines Elektrowerkzeugs verwendet, sind die Pole des Akkus mit normalen Kabelschuhen anzuschließen. Mit etwas Holz und einer Oberfräse ist es ein Leichtes, eine Halterung zu bauen in die man den Akku einfach einklicken kann, wie ich es bei meinem Entwurf getan habe.&lt;/p&gt;&#10;&lt;p&gt;Ein Griff könnte eine gute Idee für den Transport sein, und ein Gummiband könnte das Gehäuse in einer gefalteten Position halten und Boden sowie Deckel an der Rückseite fixieren.&lt;/p&gt;&#10;&lt;p&gt;Ich hoffe sehr, dass Sie die Lektüre / den Bau genossen haben und dass Sie so viel Spaß haben werden, dieses kleine Ding überall zum Spielen mitzunehmen, wie ich es tue.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Schallbert&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://blog.schallbert.de/projects/mobfobamp/"&gt;Zurück zur Projekt-Hauptseite&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>Markdown zu PDF konvertieren</title><link>https://blog.schallbert.de/md-export-pdf/</link><pubDate>Wed, 09 Jun 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/md-export-pdf/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/markdown-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: markdown to pdv converter extension for VS code"&#10; title="Markdown zu PDF konvertieren" /&gt;&#10;&lt;h3 id="die-software"&gt;Die Software&lt;/h3&gt;&#10;&lt;p&gt;Ich habe die Visual Studio Code-Erweiterung &lt;a href="https://github.com/yzane/vscode-markdown-pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Yzane&amp;rsquo;s Markdown PDF&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; gewählt. Einfach zu installieren, einfach zu benutzen, gute Dokumentation. Nur die Bilder&amp;hellip; naja&amp;hellip; wurden nicht angezeigt.&lt;/p&gt;&#10;&lt;h3 id="auch-die-bilder-werden-konvertiert"&gt;Auch die Bilder werden konvertiert&lt;/h3&gt;&#10;&lt;p&gt;Die relativen Links zu den Bildern unterscheiden sich zwischen Jekylls Interpretation und der Vorstellung des PDF-Generators. Ich musste die Markdown-Zieldatei in das Stammverzeichnis der Website verschieben und ein &lt;code&gt;.&lt;/code&gt; vor meinem &lt;code&gt;/assets&lt;/code&gt; hinzufügen, damit es funktioniert. Die Seitenumbrüche sind &amp;hellip; meh&amp;hellip; aber ich bin im Moment nicht bereit, mehr Arbeit hineinzustecken, also ist das gut genug für meine Zwecke.&lt;/p&gt;&#10;&lt;h3 id="ein-problem-mit-posts-die-nicht-angezeigt-werden"&gt;Ein Problem mit Posts, die nicht angezeigt werden?&lt;/h3&gt;&#10;&lt;p&gt;Gerade als ich diesen sechsten Beitrag von mir veröffentlicht habe, sehe ich, dass irgendwie nur Seite 2 der Beitragsübersicht angezeigt wird (sieht aus wie 5 Beiträge pro Seite) und ich kann nicht zu Seite 1 mit den &amp;ldquo;neuesten&amp;rdquo; Beiträgen wechseln: &lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-06-09_stuckonpage2.jpg" alt="terminal output" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;. Hm.&lt;/p&gt;&#10;</description></item><item><title>Jekyll: Bilder</title><link>https://blog.schallbert.de/jekyll-images/</link><pubDate>Mon, 07 Jun 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/jekyll-images/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/git_mv-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: github move operation for renaming"&#10; title="Jekyll: Bilder" /&gt;&#10;&lt;h3 id="der-trick"&gt;Der Trick&lt;/h3&gt;&#10;&lt;p&gt;Als ich einige Bilder zu meinen Inhalten hinzufügte, war alles in Ordnung, bis ich sie auf GitHub veröffentlichte. Die meisten Bilder waren verschwunden und ich konnte nicht herausfinden, warum. Eine &lt;a href="https://stackoverflow.com/questions/41468951/images-not-displaying-in-github-pages#41469181" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Suche&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; ergab, dass Github zwischen Groß- und Kleinschreibung unterscheidet und einige meiner Bildpfade nicht alle kleingeschrieben waren. Dahinter vermute ich Unterschiede zwischen Windows/Unix, da sich die beiden Betriebssysteme hier unterschiedlich verhalten.&lt;/p&gt;&#10;&lt;h3 id="ändern-von-ordnernamen-in-git"&gt;Ändern von Ordnernamen in Git&lt;/h3&gt;&#10;&lt;p&gt;Es funktionierte danach immer noch nicht, weil Git meine Umbenennung nicht erkannte. Ich musste zuerst den umbenannten Ordner in einem anderen Unterverzeichnis erstellen, &lt;code&gt;git mv folder/tArGet* target&lt;/code&gt; verwenden, um diesen an einen temporären Ort zu bringen, &lt;code&gt;tArGet&lt;/code&gt; löschen und &lt;code&gt;target&lt;/code&gt; an die Stelle verschieben, an der &lt;code&gt;tArGet&lt;/code&gt; vorher war. Nur dies hat das Problem behoben und jetzt sind github und meine lokalen Dateien wieder synchron.&lt;/p&gt;&#10;</description></item><item><title>Jekyll Inhaltsverzeichnis</title><link>https://blog.schallbert.de/jekyll-toc/</link><pubDate>Sun, 06 Jun 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/jekyll-toc/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/thissite/desktop-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Schallbert&amp;#39;&amp;#39;s Desktop"&#10; title="Jekyll Inhaltsverzeichnis" /&gt;&#10;&lt;h2 id="inhaltsverzeichnis"&gt;Inhaltsverzeichnis&lt;/h2&gt;&#10;&lt;p&gt;Es gibt eine einfache Lösung, um das Inhaltsverzeichnis der Seite zu fixieren:&#10;&lt;code&gt;toc_sticky: true&lt;/code&gt; zu den Standardeinstellungen oder dem Inhaltsverzeichnis der jeweiligen Seite hinzufügen. Wie ich das Schlüsselwort gefunden habe? Durch die Suche nach &amp;ldquo;toc&amp;rdquo; im Stammverzeichnis des Themes und ein wenig Scrollen durch die Layout-Skizzen. Ich änderte die ursprüngliche Seitennavigation in der linken Seitenleiste und tauschte sie gegen eine Dummy-Implementierung einer Galerie / Audio / Video-Inhaltsanzeige aus.&lt;/p&gt;&#10;&lt;h3 id="toc-auf-der-linken-seite"&gt;ToC auf der linken Seite&lt;/h3&gt;&#10;&lt;p&gt;Um zu verstehen, wie man das Inhaltsverzeichnis anders als in diesem Theme vorgegeben auf der linken Seite erstellt, lesen Sie bitte &lt;a href="https://blog.schallbert.de/projects/thissite/#sidebar"&gt;dieses Kapitel&lt;/a&gt; des mySite-Projekts weiter.&lt;/p&gt;&#10;&lt;h2 id="profil-des-autors"&gt;Profil des Autors&lt;/h2&gt;&#10;&lt;p&gt;Ich habe beschlossen, dass mein &lt;code&gt;author_profile&lt;/code&gt; nur auf Seiten wie &lt;a href="https://blog.schallbert.de/about/"&gt;about&lt;/a&gt;, &lt;a href="https://blog.schallbert.de/legal/"&gt;legal info&lt;/a&gt; und nicht auf den typischen Inhaltsseiten angezeigt werden soll. Also schrieb ich &lt;code&gt;author_profile: false&lt;/code&gt; für die Seiten, auf denen es nicht angezeigt werden sollte - ohne Erfolg. Warum?&#10;Ich hatte es in der &lt;code&gt;config.yml&lt;/code&gt;, Abschnitt &lt;code&gt;type defaults&lt;/code&gt; gesetzt, aber es sollte eigentlich in der Frontmatter der einzelnen Dateien stehen.&lt;/p&gt;&#10;</description></item><item><title>Jekyll `-incremental` Option</title><link>https://blog.schallbert.de/jekyll-items-update/</link><pubDate>Thu, 03 Jun 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/jekyll-items-update/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/jekyll-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Jekyll logo"&#10; title="Jekyll `-incremental` Option" /&gt;&#10;&lt;h3 id="die-kategorie-neueste-beiträge"&gt;die Kategorie &amp;ldquo;Neueste Beiträge&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;Ich habe den Grund herausgefunden, warum mein letzter Artikel auf der Landing Page auf meinem lokalen Rechner nicht angezeigt wurde, aber auf dem Server problemlos funktionierte. Es liegt daran, dass ich lokal den Befehl &lt;code&gt;$ bundle exec jekyll serve --incremental&lt;/code&gt; mit der aktiven &lt;code&gt;--incremental&lt;/code&gt; build Option verwende, um die Rebuilds für schnellere Testläufe zu beschleunigen. Aber das Problem liegt hier:&lt;/p&gt;&#10;&lt;div class="media-wrapper center"&gt;&#10; &lt;figure class="media-frame"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/2021-06-03_jekyll_incremental.jpg" alt="image: terminal output" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Bei einer inkrementellen Ausgabe scheint Jekyll den Ordner &lt;code&gt;_posts&lt;/code&gt; nicht nach neuen Einträgen zu durchsuchen, da die Seite, auf der sie erscheinen, nicht geändert sondern neu angelegt wurde.&#10;Mein Fazit ist, dass ich die Option &lt;code&gt;--incremental&lt;/code&gt; in Zukunft mit Vorsicht und nur für Änderungen bereits bestehender Artikel verwenden werde.&lt;/p&gt;&#10;&lt;h3 id="die-bilder"&gt;Die Bilder&lt;/h3&gt;&#10;&lt;p&gt;Ich habe an meinen Bildern unter &lt;code&gt;assets/images&lt;/code&gt; gearbeitet. Sie benötigen recht viel Speicherplatz, so dass die Ladezeiten der Seite zu lang werden. Ich benutze &lt;a href="https://www.getpaint.net/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;paint.net&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, um sie auf eine Größe zu schrumpfen, die ich für vertretbar halte, und speichere sie als &lt;code&gt;.jpg&lt;/code&gt; mit maximaler Kompression und einigen Kompromissen bei der Bildqualität. Auf diese Weise benötigen die meisten meiner Bilder weniger als 10% der ursprünglichen Größe in &lt;code&gt;kB&lt;/code&gt;.&lt;/p&gt;&#10;</description></item><item><title>MinimalMistakes theme - Seitendesign</title><link>https://blog.schallbert.de/jekyll-open-items/</link><pubDate>Wed, 02 Jun 2021 00:00:00 +0000</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/jekyll-open-items/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/posts/mmistakes-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: MinimalMistakes logo"&#10; title="MinimalMistakes theme - Seitendesign" /&gt;&#10;&lt;h2 id="die-landing-page"&gt;Die &amp;ldquo;Landing Page&amp;rdquo;&lt;/h2&gt;&#10;&lt;p&gt;Endlich. Indem ich den Layout-Typ auf &lt;code&gt;single&lt;/code&gt; änderte, konnte ich ein Header-Bild als Typ &lt;code&gt;overlay&lt;/code&gt; einfügen!&#10;Die Schaltflächen werden jetzt innerhalb des Bildes angezeigt. Das Bild ändert sich automatisch mit der Displaygröße und -ausrichtung aufgrund der im Ordner &lt;code&gt;_sass&lt;/code&gt; definierten visuellen Designs.&lt;/p&gt;&#10;&lt;h3 id="bild-der-hauptseite"&gt;Bild der Hauptseite&lt;/h3&gt;&#10;&lt;p&gt;Ich war dabei zu verzweifeln: meine &amp;ldquo;Landing Page&amp;rdquo; zeigte einfach kein Bild mehr an, egal wie sehr ich versuchte, die &lt;code&gt;home.md&lt;/code&gt; Datei zu ändern. Der Grund dafür war, dass es eine Standard-&amp;ldquo;index.html&amp;rdquo;-Datei gab, die mit der Vorlage geliefert wurde und die anscheinend Vorrang hatte. Nun habe ich meine Änderungen einfach direkt dort eingefügt - funktioniert einwandfrei.&#10;Übrigens, die Höhe des Overlay-Bildes wird automatisch in Abhängigkeit von Inhalt und Höhe des Overlay-Textes gewählt.&lt;/p&gt;&#10;&lt;h3 id="hinzufügen-einer-seitennavigation"&gt;Hinzufügen einer Seitennavigation&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Einfache Lösung:&lt;code&gt;toc: true&lt;/code&gt; in die YAML-Struktur des Markdown-Dokument für den gewünschten Artikel einfügen. Leider wird das Inhaltsverzeichnis statisch auf der Webseite angezeigt, sodass es beim Herunterscrollen verschwindet.&lt;/li&gt;&#10;&lt;li&gt;Schwierige Lösung: Fügen Sie die Seitennavigation in die linke Seitenleiste ein. Wie das geht? &lt;a href="https://blog.schallbert.de/projects/thissite/#navigation"&gt;Siehe hier&lt;/a&gt;.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="neueste-beiträge"&gt;Neueste Beiträge&lt;/h3&gt;&#10;&lt;p&gt;Im Moment erscheinen meine Artikel nicht in der Spalte &amp;ldquo;Neueste Beiträge&amp;rdquo; auf der Hauptseite. Das liegt daran, dass ich den Dienst neu starten müsste, weil die Beiträge statisch generiert werden &lt;a href="https://blog.schallbert.de/jekyll-items-update/"&gt;Richtig, Jekyll ist ein &lt;strong&gt;statischer&lt;/strong&gt; Seitengenerator.&lt;/a&gt;&lt;/p&gt;&#10;</description></item></channel></rss>