AdSense

Sonntag, 30. August 2020

Home Automation mit Raspberry PI und ESP8266, Teil 2: Ein einfacher Sensor (ESP8266)

(English version) Als ersten Teil möchte ich auf meine Temperatur- und Luftfeuchte-Sensoren eingehen. Ich nutze hierfür einen ESP8266, welcher einen Si7021-Sensor ausliest. Den Si7021 habe ich mit einem 4-poligen Kabel mit dem ESP8266 an den Pins D1-D4 verbunden, wie man es hier im Bild sehen kann:

Zusätzlich muss noch der Pin D0 mit RST verbunden werden, damit das Deep Sleep funktioniert, welches wir noch brauchen werden. Da ich außerdem relativ instabile Handyladegeräte für die Sensoren benutzt habe, habe ich noch einen Kondensator parallel zu G und 3V geschalten. 

Das Ganze kommt dann in ein 3D-gedrucktes Gehäuse, welches man hier auf Thingiverse finden kann: https://www.thingiverse.com/thing:4583759. Mit dem kleinen Bügel wird der Sensor im Gehäuse fixiert, danach kommt der ESP oben drauf und der Deckel fixiert dann alles. An der offenen Stelle ist nun genau genug Platz für den Anschluss des Handyladegeräts.



Der Code ist vergleichbar mit meinem anderen Post zu dem Thema, https://physudo.blogspot.com/2019/07/wlan-thermometer-mit-dem-dht22-und.html. Zuerst wird im Setup-Teil das WLan verbunden, sollte das nicht klappen resettet sich der ESP automatisch nach 100 Sekunden. Außerdem wird der Si7021-Sensor initialisiert.

In Loop wird dann, falls das WLan verbunden ist (ansonsten wird der ESP auch resettet), die Temperatur und Luftfeuchte gemessen. Sollte hier etwas schief gehen wird auch der ESP neu gestartet. Anschließend wird alles per http Request an den Raspberry gesendet, wie im vorigen Post beschrieben (https://physudo.blogspot.com/2020/08/home-automation-mit-raspberry-pi-und.html).

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
const char* ssid     = "WIFI SSID";
const char* password = "WIFI PASSWORD";

#include "Adafruit_Si7021.h"

Adafruit_Si7021 sensor = Adafruit_Si7021();

void setup()
{
  Serial.begin(9600);

  Serial.print("Your are connecting to;");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  int resetCtr = 0;
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    resetCtr ++;
    Serial.print(".");
    if (resetCtr > 200)
    {
      ESP.restart();
    }
  }

  pinMode(D0, WAKEUP_PULLUP);
  pinMode(D3, OUTPUT);
  pinMode(D4, OUTPUT);
  digitalWrite(D3, HIGH);
  digitalWrite(D4, LOW);
  delay(500);

  while (!sensor.begin())
  {
    Serial.println("Did not find Si7021 sensor!");
  }

  digitalWrite(D3, LOW);
}



void loop() {
  if (WiFi.status() != WL_CONNECTED)
  {
    ESP.restart();
  }

  if (WiFi.status() == WL_CONNECTED)
  {
    Serial.println("");
    Serial.println("Your ESP is connected!");
    Serial.println("Your IP address is: ");
    Serial.println(WiFi.localIP());

    digitalWrite(D3, HIGH);
    delay(500);
    float t = sensor.readTemperature();
    Serial.print("Temperature: ");
    Serial.println(t);
    float h = sensor.readHumidity();
    Serial.print("Humidity: ");
    Serial.println(h);
    if (isnan(t))
    {
      t = sensor.readTemperature();
    }
    if (isnan(h))
    {
      h = sensor.readHumidity();
    }
    digitalWrite(D3, LOW);

    if (isnan(t))
    {
      ESP.restart();
    }
    if (isnan(h))
    {
      ESP.restart();
    }
    

        
    HTTPClient http;
    char requestString[255] = "";
    sprintf(requestString, "http://raspberrypi/SendValues.php?name=Temperatursensor_1&split=1&data=%f,%f", t, h);    
    http.begin(requestString);
    int httpCode = http.GET();
    Serial.println(httpCode);

    if (httpCode > 0)
    {
      String payload = http.getString();
      Serial.println(payload);
    }

    http.end();

    ESP.deepSleep(300e6); 
    delay(300000);
    ESP.reset();
  }
  else
  {
    Serial.println("");
    Serial.println("WiFi not connected");
    delay(1000);
  }
}

Home Automation mit Raspberry PI und ESP8266, Teil 1: Die Grundlagen (Raspberry PI)

(English version) Heute möchte ich anfangen, mein Smart Home System zu präsentieren. Ich habe damit Anfang des Jahres angefangen und inzwischen ist es ziemlich groß gewachsen. Aktuell besteht das System aus:

  • 6 Temperatur- und Luftfeuchte-Sensoren innen
  • 3 Temperatur- und Luftfeuchte-Sensoren außen
  • 2 Temperatur- und Luftfeuchte-Sensoren in unseren zwei Terrarien
  • 1 CO2-Sensor
  • 5 schaltbaren Steckdosen
  • 3 schaltbaren Lampen
  • 1 Umschalter für die Eingänge vom Soundsystem
  • Sensoren an Waschmaschine und Trockner
  • Mikrofon und zwei Kameras am 3D-Drucker
  • Dashboard als Website an einem alten Tablet bzw. auf dem Smartphone
  • Telegram-Bot mit automatischen Benachrichtigungen und Kommandos

Die Grundlage für alles ist ein Raspberry PI 4, welcher als Server im WLAN fungiert. Hier werden alle Daten gespeichert, die Website liegt hier und alle Sensoren kommunizieren mit ihm. Auf dem Raspberry PI muss eigentlich nicht viel eingerichtet werden, ich habe den Apache2 Webserver installiert, hier findet man zahllose Tutorials, im Endeffekt ist es aber nur der folgende Befehl:

sudo apt install apache2

Außerdem habe ich den Apache2 für Php installiert, auch dazu gibt es viele Tutorials, der Befehl hierfür wäre:

sudo apt-get install php libapache2-mod-php

Das sind nun eigentlich schon alle Sachen die auf dem Raspberry PI eingerichtet werden müssen. Es empfiehlt sich aber, den Ordner /var/www/html/ als Share freizugeben, dann kann man auch von einem anderen Computer im Netzwerk auf alle Daten und Skripte zugreifen. Außerdem habe ich noch den Ordner /var/www/html/data/ angelegt, hier werden alle gespeicherten Daten abgelegt.

Damit ist schon alles eingerichtet und es kann mit den ersten Skripten losgehen. Hier möchte ich nur das grundlegende Skript zum Senden von Daten posten, alle weitere Modifikationen und Skripte werde ich dann bei den jeweiligen Posts präsentieren. Zum Senden von Daten nutze ich die Datei "SendValues.php", welche über GET-Parameter die folgenden Daten vom Sensor erwartet:

  • "name", also der Name des Sensors, z.B. "Temperatursensor_1"
  • "split", wie die Daten in verschiedenen Dateien aufgesplittet werden. Die Idee ist hier, wenn Sensoren über Jahre hinweg laufen, dass die hier nicht riesige Dateien entstehen, die man dann gar nicht mehr öffnen kann, sondern man z.B. für jeden Monat oder jedes Jahr eine eigene Datei hat. Split 0 bedeutet kein Aufsplitten, 1 bedeutet eine Datei pro Jahr, 2 eine Datei pro Monat und 3 eine Datei pro Tag
  • "data", die eigentlichen Daten, per Komma getrennt. z.B. für einen Temperatur- und Luftfeuchte-Sensor: 24.1,54.6

Das Skript besteht nun aus folgenden Teilen. Zuerst der Header:

<html>
 <head>
  <title>Send Sensor Values</title>
 </head>
 <body>
  <?php 

Danach beginnt der eigentliche Code. Hier werden die Daten gelesen und das aktuelle Datum bestimmt:

$name = htmlspecialchars($_GET["name"]);
$split = htmlspecialchars($_GET["split"]);
$data = htmlspecialchars($_GET["data"]);

$date = gmdate("Y-m-d") . "T" . gmdate("H:i:s.u") . "Z";

Als nächstes wird eine [name]_last.csv-Datei erstellt, welche den aktuellsten Wert hat:

$file = "data/".$name."_last.csv";
$Saved_File = fopen($file, 'w');
fwrite($Saved_File, $date . "," . $data . "\r\n");
fclose($Saved_File);

Als nächstes wird nun das Splitten gemacht. Dazu wird der Dateiname erstellt, [name]_[Jahr]-[Monat]-[Tag].csv. Da ich einen Überlapp zwischen den verschiedenen Dateien haben will erstellt er auch gleich die nächste Datei, also für nächstes Jahr, nächsten Monat, nächsten Tag, und schreibt hier auch den Wert rein, das sorgt dafür, dass wenn ich mir das letzte Jahr plotten möchte, ich nicht nur Werte bis zum 1.1. habe, sondern auch von dem Jahr davor, sodass der Zeitraum von einem Jahr auf jeden Fall in der Datei enthalten ist. $file1 ist die aktelle Datei, $file2 die für den nächsten Zeitraum.

$file = "data/".$name.".csv";

if ($split == "1")
{
  $file = "data/".$name."_" . gmdate("Y") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y", strtotime("+1 year")) . ".csv";
}
if ($split == "2")
{
  $file = "data/".$name."_" . gmdate("Y-m") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y-m", strtotime("+1 month")) . ".csv";
}
if ($split == "3")
{
  $file = "data/".$name."_" . gmdate("Y-m-d") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y-m-d", strtotime("+1 day")) . ".csv";
}

Anschließend wird das nun in die Dateien gespeichert. Falls "split" 0 ist, wird auf das Speichern in die zweite Datei verzichtet, da es ja für den gesamten Zeitraum nur eine Datei gibt.

$Saved_File = fopen($file, 'a');
fwrite($Saved_File, $date . "," . $data . "\r\n");
fclose($Saved_File);
if ($split == "0")
{
}
else
{
  $Saved_File = fopen($file2, 'a');
  fwrite($Saved_File, $date . "," . $data . "\r\n");
  fclose($Saved_File);
}

Und das war auch schon der Code, man muss jetzt noch die ganzen Tags wieder zu machen, und schon ist SendValues.php fertig:

  ?>
 </body>
</html>

Das ist nun eigentlich schon alles, was man auf der Server-Seite benötigt, als nächstes können die Sensoren ihre Werte senden. Zum Ausprobieren kann man das auch über den Browser im Heimnetz testen, dabei sollten die Dateien Temperatursensor_1_last.csv, und noch zwei weitere Dateien mit dem aktuellen und dem kommenden Jahr als Endung erstellt werden:

http://raspberrypi/SendValues.php?name=Temperatursensor_1&split=1&data=25.3,64.59

Für alle, die sich das nicht einzeln kopieren möchten hier nochmal der gesamte Quellcode:

<html>
 <head>
  <title>Send Sensor Values</title>
 </head>
 <body>
  <?php
 
$name = htmlspecialchars($_GET["name"]);
$split = htmlspecialchars($_GET["split"]);
$data = htmlspecialchars($_GET["data"]);

$date = gmdate("Y-m-d") . "T" . gmdate("H:i:s.u") . "Z";

$file = "data/".$name."_last.csv";
$Saved_File = fopen($file, 'w');
fwrite($Saved_File, $date . "," . $data . "\r\n");
fclose($Saved_File);

$file = "data/".$name.".csv";

if ($split == "1")
{
  $file = "data/".$name."_" . gmdate("Y") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y", strtotime("+1 year")) . ".csv";
}
if ($split == "2")
{
  $file = "data/".$name."_" . gmdate("Y-m") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y-m", strtotime("+1 month")) . ".csv";
}
if ($split == "3")
{
  $file = "data/".$name."_" . gmdate("Y-m-d") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y-m-d", strtotime("+1 day")) . ".csv";
}
$Saved_File = fopen($file, 'a');
fwrite($Saved_File, $date . "," . $data . "\r\n");
fclose($Saved_File);
if ($split == "0")
{
}
else
{
  $Saved_File = fopen($file2, 'a');
  fwrite($Saved_File, $date . "," . $data . "\r\n");
  fclose($Saved_File);
}

  ?>
 </body>
</html>

Dienstag, 11. August 2020

Machine Learning für einen Kamera-überwachten 3D-Drucker

(English version) Nach über einem Jahr seit meinem letzten Post möchte ich heute endlich wieder eines meiner Projekte vorstellen. Im letzten Jahr habe ich an einigen Sachen gearbeitet, vor allem dem Upgrade meines Ultimakers 2+ auf 5 verschiedene Farben. Der Post heute tangiert das Thema, ist aber gleichzeitig auch teil meines IoT-Systems: Eine kamerabasierte Überwachung für einen 3D-Drucker mit automatischer Zustandserkennung.

Zuerst zu den Kameras. Ich habe zwei EPS32-Cam module, wie diese programmiert werden ist bereits im Internet ausführlich erklärt. Diese habe ich an meinem 3D-Drucker angebracht, die erste Kamera von oben, mit einem 3D-gedruckten gestell welches man hier herunterladen kann: https://www.thingiverse.com/thing:3899159

Die zweite Kamera is im 3D-Drucker mit Klebeband fixiert und hat unter dem Kameramodul noch einen kleinen 45°-Block um im richtigen Winkel zu stehen.

Die Kameras erzeugen nun etwa alle 3 Minuten ein neues Bild, insofern hat sich inzwischen schon eine gute Menge an Trainingsdaten angesammelt. Die beiden Blickwinkel sehen so aus:



Die Bilder können in verschiedene Kategorien klassifiziert werden:

  1. Leer: Der Druckkopf ist an seiner normalen Position und das Druckbett ist leer
  2. Vorbereiten: Der Druckkopf ist vorne links und der Drucker heizt sich auf
  3. Drucken: Das versteht sich von selbst
  4. Fertig: Der Druckkopf ist an seiner normalen Position und das Druckbett ist nicht leer
  5. Problem: Ein Abstand zwischen Druckbett bzw. Druckobjekt und Düse ist erkennbar
  6. Wartung: Meine Hände sind sichtbar, der Druckkopf ist zerlegt oder nicht an seiner normalen Position
  7. Aus: Alle Lichter sind aus und die Bilder sind schwarz

Kamera 1 kann den Unterschied zwischen Drucken und Problem nicht erkennen, also erkennt diese Kamera die Kategorie "Problem" nicht, Kamera 2 kann nicht zwischen Leer und Fertig unterscheiden, also kann diese Kamera die Kategorie "Fertig" nicht erkennen.

Ich habe die ersten Bilder die ich hatte dann in die 7 Kategorien sortiert und dann ein neuronales Netz darauf trainiert. Meine Architektur des Netzes (in Tensorflow, Python) sieht folgendermaßen aus:

model = models.Sequential()
model.add(layers.Conv2D(32, (5, 5), activation='relu', input_shape=(128, 128, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(32, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(32, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(32, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(32, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(7))

Das Netz ist für einen Input mit 128x128 Pixeln mit jeweils 3 Farbkanälen ausgelegt und besitzt 5 convolutional Layers, welche jeweils ein max Pooling nachgeschaltet haben. Danach gibt es noch ein fully connected Layer mit 64 Neuronen welches dann zum output Layer führt, welches 7 neuronen hat.

Um die Erkennung weiter zu verbessern habe ich ein zweites Netz erstellt, welches Bilder der Größe 64x64 nutzt und daher nur 4 convolutional Layers hat. Die Ergebnisse der Netze in Prozent werden dann addiert (und durch 2 gteilt), dies verbessert die Genauigkeit weiter. Am Ende wird nun das Ergebnis beider Kameras addiert, zusätzlich dazu habe ich noch ein Mikrofon im Drucker, welches über eine Messung des Geräuschlevels erkennt, ob der Lüfter im Druckkopf an ist, sodass ich hier noch einen weiteren Input habe. Das Gesamtergebnis aus beiden Kameras und dem Mikrofon liefert mir dann eine Vorhersage für den Status des Druckers. Wie im vorherigen Post erklärt wird dieses Ergebnis dann an mein IoT-System gesendet, welches mir eine Nachricht schickt, falls

  • Der Drucker seinen Status zu "Problem" ändert
  • Der Drucker seinen Status zu "Fertig" ändert

Dienstag, 2. Juli 2019

WLAN-Thermometer mit dem DHT22 und EPS8266-12E

(English version) Eigentlich sollte ich ja an den Posts zum Drucker weitermachen, aber ich habe gerade an einem ESP8266-12E gearbeitet und dabei super einfach ein WLAN-Thermometer (mit dem ESP8266-12E als Web-Client, welcher an einen Apache-Server auf meinem Raspberry PI sendet) realisiert, sodass ich das erstmal hier erklären möchte.


1. Der ESP8266-12E
Der ESP8266-12E ist ein Arduino-ähnlicher Baustein, welcher einen WLAN-Baustein mit integriert hat. Sucht man auf Google danach, findet man diesen bei vielen Quellen zu kaufen. Das Tolle ist, dass er mit der Arduino-Umgebung programmiert werden kann, sodass alle Libraries auch funktionieren!

2. Einrichtung Arduino für den ESP8266-12E
Zuerst sollte der CH340-Treiber installiert werden. Da es dazu genug im Internet gibt werde ich darauf nicht eingehen. Als nächstes muss in der Arduino-Umgebung auf die Voreinstellungen gehen:

Hier trägt man nun die zusätzliche Boardverwalter-URL ein:
http://arduino.esp8266.com/stable/package_esp8266com_index.json

Danach kann man dann auch schon im Boardverwalter die Einstellungen für die Boards herunterladen:

Hier muss nun nach "ESP8266" gesucht werden und das sollte dann auch installiert werden:

Nun ist eigentlich alles vorbereitet und wir können das Board auswählen, ich habe dabei "NodeMCU 1.2 (ESP8266-12E Module)" ausgewählt:


3. Der Code
Zu dem Code gibt es eigentlich nicht viel zu erklären. Einerseits muss man seine WLAN-Daten eingeben, darunter wird der DHT22-Sensor initialisiert. In "Loop" wird getestet ob man mit dem WLAN verbunden ist, sollte dies der Fall sein, so wird Temperatur und Luftfeuchte gemessen (float t und float h), diese Werte werden in einen String (requestString) geschrieben und dann als HTTP Get-Request an einen Webserver gesendet. Den Code für den Web-Server habe ich unten angefügt.

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
const char* ssid     = "***";
const char* password = "***";   
int wifiStatus;
    
#include "DHT.h"
#define DHTPIN D2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(9600);
  // put your setup code here, to run once:

  Serial.print("Your are connecting to;");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }

  dht.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  wifiStatus = WiFi.status();
  
  if(wifiStatus == WL_CONNECTED){
    Serial.println("");
    Serial.println("Your ESP is connected!");  
    Serial.println("Your IP address is: ");
    Serial.println(WiFi.localIP()); 
    float t = dht.readTemperature(); 
    float h = dht.readHumidity(); 
    HTTPClient http;
    char requestString[255] = "";
    sprintf(requestString, "http://webServer.de/Send.php?name=TemperatureSensor&data=%f,%f", t, h);
    http.begin(requestString);  //Specify request destination
    int httpCode = http.GET();                                                                  //Send the request
    
    if (httpCode > 0) 
    { //Check the returning code
      String payload = http.getString();   //Get the request response payload
      Serial.println(payload);                     //Print the response payload
    }
    
    http.end();   //Close connection
    
    delay(300000);
  }
  else{
    Serial.println("");
    Serial.println("WiFi not connected");
    delay(1000);
  }
}

4. Der Server-Code
Auf meinem Raspberry Pi läuft ein Apache-Webserver. In /var/www liegt die Datei "Send.php", welche einfach Sensordaten annimmt und in .csv-Dateien schreibt. Dabei übergibt der Sensor im Get-Befehl seinen Namen, das wird dann auch der Dateiname, hier wird dann mit aktuellem Zeitstempel der Inhalt von "data" angehängt, was in diesem Fall die Temperatur in °C und die Luftfeuchte in % ist.

<html>
 <head>
  <title>Send Sensor Values</title>
 </head>
 <body>
  <?php

$name = htmlspecialchars($_GET["name"]);
$data = htmlspecialchars($_GET["data"]);

$date = date("Y-m-d") . "T" . date("H:i:s.u") . "Z";

$file = "data/".$name.".csv";
$Saved_File = fopen($file, 'a');
fwrite($Saved_File, $date . "," . $data . "\r\n");
fclose($Saved_File);

  ?>
 </body>
</html>

Donnerstag, 20. Juni 2019

2.85mm-Filament Bowden Extruder Getriebe-Feeder

(English version) Wie angekündigt werde ich nun die Einzelteile meines Multimaterial-Ultimakers beschreiben. Heute widme ich mich dem Feeder. Leider habe ich keine gute fertige Lösung gefunden, deshalb musste ich aus teils gekauften Teilen den Feeder selber bauen. Der Feeder besteht aus einigen 3D-gedruckten Teilen.

Für den Extruder werden folgende Teile benötigt:
  • 3D-Druck Teile: https://www.thingiverse.com/thing:3703063
  • Einige Teile von ebay oder anderen Shops (hier am besten anhand der Bilder unten die richtigen Teile kaufen)
    • Ein 1,75 mm Metall-Feeder ("MK8 Metal Bowden Extruder" bei ebay)
    • Ein 4/6 mm Bowden-Schlauch (PTFE, Innendurchmesser 4 mm, Außendurchmesser 6 mm)
    • Ein Bowden-Coupler (vergleichbar mit den Originalteilen beim Ultimaker 2+, "Bowden tube clip" bei ebay)
    • Extruder-Vorschubrad
    • 2 x 26-zähniges Extruder-Vorschubrad
    • 2 x Kugellager 625ZZ (16x5x5 mm)
    • Nema17-Motor, 1,7A, 0,9°
  • Schrauben
    • 3 x M3x10
    • 3 x M3x30 (besser sind eher längere Schrauben, also z.B. 35 mm Länge)
    • M4x30
  • Alu-Stange, 5 mm Durchmesser (bei mir gab es nur 5,3, das passt aber auch)
Zuerst wird nun der Motor am Hauptgestell befestigt.

Als nächstes muss beim Feeder ein Loch aufgebohrt werden, dafür nehme ich einen 5er Metallbohrer. Anschließend sollten die Kanten etwas abgerundet werden.

Danach muss die Schraube, auf welche die kleine Feder gesteckt wird, gekürzt werden, da sonst die zwei sich gegenüberliegenden Schrauben berührer bevor die Feder genug Druck aufbaut.

Als nächstes wird nun auch der Bowden-Coupler montiert und der Bowden-Schlauch montiert.

Danach wird der Feeder mit einer M3x10-Schraube an das Hauptgestell geschraubt, von unten wird dabei ein Kugellager in die dafür vorhergesehene Halterung gelegt und dies alles zusammen mit der Schraube befestigt. Dabei sollte man achten, dass der Kopf der Schraube im Metallteil liegt, sonst könnte es später Probleme geben.


Als nächstes wird der Arm des Feeders zusammengebaut (Hier wird auch die M4-Schraube eingebaut) und montiert, dafür werden nun drei M3x30-Schrauben in entgegengesetzter Richtung wie die M3x10-Schraube eingesetzt. Danach wird die Feder für den Arm eingesetzt.



Danach wird ein 52 mm langes Stück von dem Alu-Stab abgeschnitten und mit 7 mm Abstand zu einer Seite das Vorschubrad montiert.

Auf dieses Stück wird nun die lange Hülse geschoben, danach wird es von oben durch das Kugellager geschoben. Hier muss man eventuell den Hebel unter der Feder etwas drücken, damit es hinein passt.

Falls man die M3x30-Schrauben davor mit Muttern fixiert hat, sollte man diese Muttern jetzt entfernen. Als nächstes wird das zweite Kugellager auf die Achse gesteckt und der passende Halter dafür von oben aufgesetzt. Das Ganze kann man nun an der unteren Schraube fixieren, wenn man etwas Druck auf die Feder über die M4-Schraube ausübt fallen die anderen Schrauben nun auch nicht mehr heraus.

Danach wird noch der große Halteclip montiert, hier können nun die beiden anderen Muttern angeschraubt werden

Als nächstes geht es auf der anderen Seite weiter. Dabei werden die zwei Vorschubräder mit 26 Zähnen in die großen Zahnräder montiert, dabei kann man einen kleinen Tropfen Klebstoff aufsetzen, damit die Zahnräder nicht entlang der Achse auseinanderrutschen. Die kleine Hülse wird auf die Achse gesteckt, ansonsten reibt das große Zahnrad auf dem Kunststoffteil, welches das Kugellager hält und fängt an, mit diesem Teil zusammenzuschmelzen. Die Zahnräder werden nun auf die Achse bzw. die Achse vom Motor montiert, dabei sollte ein wenig Gegendruck von unten auf die Achse ausgeführt werden, damit es kein Spiel gibt und etwas anfängt, zu wackeln.

Und damit ist der Extruder fertig und kann eingesetzt werden. Der Extruder kann natürlich auch einfach so für 3D-Drucker eingesetzt werden und muss nicht zwingend im Zusammenhang mit einem Mehrfarbendruck stehen.

Sollte ich irgendeinen Schritt nicht sauber erklärt haben oder falls ich Materialien benutze, von denen ich oben nichts geschrieben habe, bitte einfach kurz Bescheid geben, dann passe ich das an.

Sonntag, 16. Juni 2019

Vollautomatischer Mehrfarbendruck mit dem Ultimaker 2+

(English version) Wie im letzten Post vom November letzten Jahres angekündigt habe ich mich mit dem Mehrfarbendruck mit dem Ultimaker 2+ beschäftigt. Inzwischen habe ich nach vielen Monaten Arbeit eine Lösung gefunden, um mit einem Ultimaker 2+ mit beliebig vielen Farben zu drucken. In diesem Post möchte ich die einzelnen Komponenten davon kurz beschreiben und dann in den folgenden Posts jeweils darauf eingehen.

Das Grundprinzip ist ähnlich wie beim halbautomatischen Mehrfarbendruck, es wird im Gegensatz zum Ultimaker 3 nur eine Nozzle benutzt und bei einem Farbwechsel wird das Material herausgezogen und das nächste Material hineingeschoben. Dafür habe ich hinter dem Extruder einen Y-Splitter angebracht, in welchem mehrere Filamentschläuche zusammenlaufen. Am Ende von jedem dieser Schläuche befindet sich dann ein eigenen Feeder, sodass ich für jede Farbe, mit der ich gleichzeitig drucken möchte, einen extra Feeder bauen musste. Beim Herausziehen von einem Material kodiert der Drucker dabei den nächsten benötigten Extruder in der Bewegung, sodass eine externe Elektronik erkennt, welcher Motor als nächstes angeschaltet werden soll und schaltet diesen dann über eine Relaiskarte an. Das Gesamtprojekt gliedert sich also in die folgenden Teile:
  1. Y-Splitter für die Filamentschläuche
  2. Multiple Feeder
  3. Externe Elektronik zum Umschalten der Extruder
  4. Skripte in Slic3r
Punkt 1 ist relativ leicht gelöst, dazu habe ich Designs für 4, 8 und 16 Filamentschläuche erstellt und diese auf Thingiverse hochgeladen. Eventuell muss hier bei den Druckeinstellungen (Horizontal expansion) gespielt werden, dass die Adapter gut passen.
https://www.thingiverse.com/thing:3695457

Samstag, 10. November 2018

Halbautomatischer Mehrfarbendruck mit dem Ultimaker 2+

(English version) Sehr lange, ziemlich genau 2 Jahre ist hier nichts mehr passiert - das will ich nun ändern. Ich arbeite momentan an einem Projekt, welches ich auf jeden Fall teilen will: Ich rüste meinen Ultimaker 2+ um, sodass ich mehrere Farben drucken kann, bis zu 15 ist da der aktuelle Plan. Dazu werde ich in Zukunft, vor allem wenn ich den Umbau überhaupt ersteinmal vorgenommen habe, mehr schreiben. Heute möchte ich aber schonmal die Vorstufe davon präsentieren: Halbautomatischer Mehrfarbendruck mit dem Ultimaker 2+. Die Grundidee ist dabei sowohl beim vollautomatischen als auch beim halbautomatischen Mehrfarbendruck, dass nur eine Nozzle benutzt wird und der Drucker immer das alte Filament herauszieht und dann das neue durchdrückt, vergleichbar mit dem Prusa i3 Multi-Material Upgrade, hier wird auch nur eine Nozzle verwendet.

Zuerst einmal: Was meine ich mit halbautomatisch? Ganz einfach: Der Drucker druckt eine Farbe, irgendwann wirft er das Material aus und wartet, bis ich das andere Material hineingetan habe. Damit kann man einfache Sachen drucken, welche z.B. aus einer Grundfarbe bestehen und ab einer gewissen Höhe eine andere Farbe haben - Beispiele wären hierfür z.B. Schrift auf einem Objekt, ein Leitkegel (https://www.thingiverse.com/thing:3207478) oder z.B. aus dem Bereich des Geländebaus dieses "Gemüsebeet" (https://www.thingiverse.com/thing:3207486).

Was musste ich dafür anpassen? Am Drucker? Gar nichts! Das ist das Tolle. Als Slicer habe ich nun "Slic3r" genommen, da ich hier benutzerdefinierten G-Code einfügen kann und die Software theoretisch beliebig viele Extruder unterstützt. Ich werde hier jetzt Schritt für Schritt erklären, wie meine Druckprofile in Slic3r aussehen. Zuerst die Printer Settings in Slic3r:

Hier habe ich jetzt für jeden Extruder das Folgende eingestellt:

Im Bereich Custom G-code habe ich die folgenden Sachen stehen:
Start G-code
M109 S[first_layer_temperature] ; heat nozzle to first layer temperature and wait until reached
G21 ; metric values
G90 ; absolute positioning
M82 ; Extruder to Absolute
G28 ; home all axes
G1 X7 Y7 F6000 ; bring extruder to the front
G1 Z25 F2000 ; Raise Bed to Head
G92 E0 ; zero the extruded length
T0 ; Select Extruder 1
G92 E0 ; Zero Extrude L
G1 F200 E8 ; Extrude 16 @ 200mm/s
G92 E0 ; Zero Extrude L
G1 F50 E4 ; Extrude 16 @ 50mm/s
G92 E0 ; Zero Extrude L
G1 F20 E2 ; Extrude 2 @ 20mm/s
G1 X100 Y7 F6000 ; bring extruder to the front
G92 E0 ; Zero Extrude L
G92 E0 ; zero the extruded length again
End G-code
; Add Change Tool To Extruder 0 here

G92 E0 ; zero the extruded length
G1 E-2 F200 ; Extrude(Retract) -2mm @ 50mm/s
G28 ; home all
M104 S0 ; turn off heaters
M140 S0 ; turn off bed
M84 ; disable motors
Before layer change G-code
;XXX Before Layer Change
After layer change G-code
;LayerChange
G90 ; absolute positioning
G1 X190 Y190 F6000;

G92 E0 ; zero the extruded length
;G1 E7 F2100 ; Retraction compensation


;Prime Tower

;Prime Tower Routine
G91; Relative mode
G1 Z-1 F2100; Lower Head 1mm
G90 ; absolute positioning
G1 X180 Y190 F6000;
G91; Relative mode

;Rectangle around
G1 Y16 E7.1 F600
G1 X-128 E1 F1200
G1 Y-16 E0.1
G1 X128 E1
G1 Y-16 E0.1
G1 X-128 E1
G1 Y16 E0.1

;Prime Nozzle
;G1 X-128 F3600;No extrusion because part of border


G1 X16 Y-16 F6000
G1 Y32 E0.1 F1200
G1 X16  F6000
G1 Y-32 E0.1 F1200
G1 X16 F6000
G1 Y32 E0.1 F1200
G1 X16 F6000
G1 Y-32 E0.1 F1200
G1 X16 F6000
G1 Y32 E0.1 F1200
G1 X16 F6000
G1 Y-32 E0.1 F1200
G1 X16 F6000
G1 Y32 E0.1 F1200
G1 X16 F6000
;G1 E-7 F2100 ; Retract
G1 Y-32 E-6.9 F1200;No extrusion because part of border

G1 Z1 F2100; Raise Head 1mm

G90 ; absolute positioning
G92 E0 ; zero the extruded length


;Retract
;G92 E0
;LayerEnd
Tool change G-code
;XXX Tool Change
;M104 S100
G90 ; absolute positioning
G1 X190 Y190 F6000;

G92 E0 ; zero the extruded length
G1 E-399 F10000 ; Extrude(Retract) -500mm @ 50mm/s
G1 E-40[next_extruder] F30 ; Extrude(Retract) -1mm @ 1mm/s
G1 E-715 F10000 ; Extrude(Retract) -500mm @ 50mm/s

G92 E0 ; zero the extruded length
G1 F50 E-40 ; Extrude 8 @ 50mm/s

M0;

;M104 S[temperature_0]
G90 ; absolute positioning
G92 E0 ; zero the extruded length
G1 F50 E20 ; Extrude 8 @ 50mm/s

G92 E0 ; zero the extruded length
G1 E700 F10000 ; Extrude(Insert)500mm @ 50mm/s

G92 E0 ; zero the extruded length
G1 F50 E20 ; Extrude 16 @ 50mm/s


;Prime Tower

;Prime Tower Routine
G91; Relative mode
G1 Z-1 F2100; Lower Head 1mm
G90 ; absolute positioning
G1 X180 Y190 F6000;
G91; Relative mode

;Prime Nozzle
G1 Y1 F6000
G1 Y[next_extruder] F6000
G1 X-128 E2 F700
G1 Y-16 F6000
G1 X128 E2 F700
G1 Y-1 F6000
G1 Y-[next_extruder] F6000
G1 X-128 F6000

G1 X1 F6000
G1 X[next_extruder] F6000
G1 Y32 E0.2 F700
G1 X16 F6000
G1 Y-32 E0.2 F700
G1 X16 F6000
G1 Y32 E0.2 F700
G1 X16 F6000
G1 Y-32 E0.2 F700
G1 X16 F6000
G1 Y32 E0.2 F700
G1 X16 F6000
G1 Y-32 E0.2 F700
G1 X16 F6000
G1 Y32 E0.2 F700
G1 X16 F6000
G1 Y-32 E-6.8 F700

G1 Z1 F2100; Raise Head 1mm

G90 ; absolute positioning
G92 E0 ; zero the extruded length


;Retract
;G92 E0
;ToolEnd
Als nächstes kommen die Filament Settings für Filament und Cooling:







 
Als nächstes kommt nun der größte Block, nämlich die Print Settings mit allen Unterkategorien:














Hier sollte natürlich der richtige Pfad zu dem entsprechenden Skript eingetragen werden. Ich musste nämlich ein Post-Script schreiben, welches meinen G-Code bearbeitet, da Slic3r T0, T1, ... für die verschiedenen Extruder verwendet - ich aber nur einen benutze. Füttert man den Ultimaker mit T1, so bewegt sich der erste (und einzige) Extruder nicht mehr, was in diesem Fall ja der falsche Ansatz wäre. Das Post-Script tut 2 Dinge:
  • Alle T1, T2, ... zu T0 ändern
  • Ein Problem mit der Retraction beheben - Slic3r macht eine Retraction bei Tools die ich nicht benutze bzw. schiebt das Material nach dem Custom G-Code wieder in die Nozzle. Das macht er aber nicht, wenn ich ein Tool zum ersten Mal verwende, da Slic3r ja in diesem Tool auch kein Material herausgezogen hat. Daher checkt mein Post-Script, ob in der entsprechenden Zeile nach einem Tool Change ein E7-Befehl ist, also ob das Material 7mm wieder in die Nozzle gedrückt wird, falls nicht fügt das Post-Script dies hinzu
Das Post-Script ist in Octave (Matlab-ähnlich) geschrieben, das sollte man sich also ebenfalls installieren. Mein PostScript "StartOctave.bat" sieht so aus:
C:\Octave\Octave-4.2.1\octave.bat C:\Slic3rSkripte\OctavePostScript.m %1
Es ruft also die Datei "OctavePostScript.m" auf, welche so aussieht:
s = argv(){1};

myfile = fopen("testOctave.txt", "w");
fprintf(myfile, "%s\r\n", s);
fclose(myfile);

%s = "I:\Traffic_Cone_Rings_Orange.gcode";
myfile = fopen (s, "r");
outText = [];
tline = fgetl(myfile);
i = 1;
outText{i} = tline;
while ischar(tline)
  i = i+1;
  outText{i} = tline;
 
 
  tline = fgetl(myfile);
end
imax = i;
fclose(myfile);

myfile = fopen(s, "w");
%fprintf(myfile, ";EDITED!\r\n");
insertLine = 0;
for i=1:imax
  tempLine = outText{i};
 
  if (size(tempLine,2) > 1)
    if (tempLine(1) == "T")
      outText{i} = "T0";
    end
  end
 
 
  if (strcmp(tempLine,";ToolEnd"))
    insertLine = 7;
  end
  
  if (insertLine > 0)
    insertLine = insertLine -1;
  end
 
  if (insertLine == 1)
    if (tempLine(4) == "E")
    else
      fprintf(myfile, "G1 E7.00000 F2100.00000\r\n");
      fprintf(myfile, "G92 E0 \r\n");
    end
  end

  fprintf(myfile, "%s\r\n", outText{i});
end
fclose(myfile);
Nun muss also nur noch ein Objekt in Slic3r geladen werden, über Rechtsklick-Settings kann man dann mit "Load Part" die weiteren Farb-Objekte dazu laden und dann den jeweiligen Extruder bzw. die jeweilige Farbe einstellen. Für Tests könnte man also die Thingiverse-Links oben von mir nutzen.

Der Quellcode ist definitiv noch ausbaufähig, wer Verbesserungsvorschläge hat kann diese mir gerne mitteilen, man sollte das alles auch unter dem Hintergrund betrachten, dass ich bereits versuche, alle auf einen automatischen Materialwechsel auszulegen, sodass einige Sachen vielleicht teilweise etwas zu kompliziert aussehen könnten.