Raspberry Pi Gehäuse (für 80mm Lüfter, 3D gedruckt)

Eines vorweg, dass ist mein erstes 3D-Druck Projekt.

TL:TR

Ein Raspberry Pi 3 unter Last braucht eine aktive Kühlung, sonst drosselt er sich. Normale Gehäuse bieten maximal 40mm Lüfter, die sind zu laut und lassen sich nicht regeln. Dies ist mein Entwurf für ein Gehäuse mit Platz für einen 4-Pin PWM 80mm Lüfter.

Ausgangssituation

Ich betreibe mehrere Webdienste intern in meinem lokalen Netzwerk zu Hause. Um diese Dienste aus dem Internet erreichbar zu machen, betreibe ich einen kleinen Reverse-SSL-Proxy. Der Proxy hat zwei Aufgaben, erstens werden die internen Webdienste unter einer URL (mit verschiedenen Pfaden) zusammengefasst und aus dem Internet verfügbar gemacht. Zweitens werden alle intern unverschlüsselten Webdienste nach aussen über https angeboten.

Das Problem

Der Reverse-SSL-Proxy läuft auf einem Raspberry Pi 3B in einem kleinen Gehäuse mit Wandbefestigung. Jeder Zugriff auf einen internen Webdienst muss durch diesen kleinen ARM-Rechner und erzeugt damit CPU-Last, dies bedeutet Abwärme. Ein kleiner Test mit dem Apache HTTP server benchmarking tool zeigt, dass bereits nach weniger als hundert Anfragen, die CPU des Raspberrys so heiß wird, dass er beginnt sich selbst zu drosseln. Zusammengefasst: Ein Raspberry Pi 3B+ unter Last benötigt einen Lüfter. In den gängigen Gehäusen passt maximal ein 40mm DC Lüfter. Die kleinen Dinger sind laut und lassen sich schlecht regeln. Zum diesem Zeitpunkt gab es auf thingiverse.com leider nur Gehäuse für wirklich große Lüfter (ab 120mm), meist wird dort noch eine Schrittmotorensteuerung für CNC-Fräsen oder 3D-Drucker mit gekühlt. Das ist zu groß für meine Zwecke.

Ein anderes Problem ist die Stromversorgung. Der Pi 3b(+) möchte mit mindestens 2.5A bei 5V versorgt werden. Der Pi 3b(+) ist da sehr viel anspruchsvoller als seine Vorgägner, bei schlechter Stromversorgung gibt es schnell eine ‘undervoltage’ Warnung. Gute USB-Kabel sind sehr wichtig, allerdings habe ich bisher kein Kabel gefunden das länger als 2m ist und den Pi stabil versorgt. Das liegt unter anderem am Innenwiderstand der USB-Kabel, an der niedrigen Spannung und daran das die gängigen Netzteile die Spannung nicht nachregeln.

Die Idee

Ich bräuchte ein Gehäuse, mit Platz für einen flachen 80mm 4-Pin PWM Lüfter und einen StepUp-Wandler (12V zu 5V/3A). Wandmontage sollte möglich sein, am besten so das man noch an die MicroSD-Karte kommt. Der Lüfter sollte nicht direkt mit dem Gehäuse verschraubt werden, besser ist eine Verbindung mit Gummi. Lüftungsschlitze müssen vorhanden sein, möglichst nicht oben, es soll ja nicht ins Gehäuse stauben.

Der Entwurf

Der Pi braucht 85x56mm Grundfläche, um ihn dann noch mit Strom zu versorgen, braucht man noch etwas Platz für den Micro-USB-Stecker. Das heißt der Pi wird nicht symetrisch in der Mitte liegen, sondern am rechten Rand. Der 80mm 4-Pin PWM Lüfter ist etwas über 10mm hoch und es muss noch etwas Platz für die Gummidämpfer eingeplant werden. Mit diesen Rahmenbedingungen und meinem Wunsch nach geraden Werten, ergibt sich eine ungefähre Gehäusegröße von 100x100x50mm.

Dann brauche ich Halterungen für den Pi, Aussparungen für USB, Netzwerk, MicroSD-Karte und einen Rundstecker (12V Versorgungsspannung). Lüftungsschlitze an den Seiten und im Deckel und irgendetwas um den Lüfter zu befestigen und um den StepUp-Wanlder zu fixieren.

Die Software

Ich hatte bisher noch nie selbst mit 3D-Modelierung zu tun. Die meisten Anwendungen sahen für meinen Geschmack zu kompliziert aus. Ansprechend und einfach sah TinkerCAD von Autodesk aus. Ich war zuerst etwas skeptisch, da die Anwendung komplett im Browser läuft. Insgesamt bin ich positiv überrascht worden. Das Konzept und die Bedienung sind definitv an Anfänger gerichtet.

Im Grunde zieht man einfache geometrische Objekte, wie Quader, Kugeln oder Zylinder von der Seite auf die Arbeitsfläche und kann dessen Grösse dann in allen Dimensionen anpassen. Man bestimmt ob die Objekte massiv sind oder Aussparungen haben und dann kann man die Objekte verschmelzen.

Zur Ausrichtung der Objekte kann man nur zentriert, sowie links- oder rechtsbündig wählen. Genau hier merkt man dann auch die Zielgruppe, ein Objekt millimetergenau zu positionieren geht nur über selbst erstellte Hilfsobjekte.

Das Model

Die Konstruktion war recht straightforward. Als erstes habe ich auf der 100x100mm Grundfläche die Halterungen für den Pi positioniert. Zur Befestigung drückt man [M2,5] Muttern in die Unterseite der Grundplatte, dann lässt sich der Pi mit Schrauben von oben fixieren. Die Grundplatte brauchte dann noch eine Aussparung, um von aussen an die MicroSD-Karte zu kommen und schließlich noch ein paar Laschen für Kabelbinder, hier wird später der StepDown-Wandler zur Stromversorgung befestigt. In die Seitenteile habe ich Lüftungsschlitze eingefügt und in der Vorderseite Aussparungen für Netzwerk, USB und eine 5,5x2,1mm Hohlbuchse.

Der Deckel braucht auch Lüftungsschlitze, hier habe ich eine Wabenstruktur gewählt, da dies wohl etwas stabiler ist und besser aussieht. Innen braucht man kleine Taschen um die Gummidämpfer zur Lüfterfixierung zu befestigen. Fertig.

3D-Model

Der Druck

Ich selbst habe noch keinen 3D-Drucker, deshalb war meine erste Idee das Model bei einem der vielen kommerziellen Anbieter im Internet drucken zu lassen. Kurzum - das war mir zu teuer, vor allem, weil es sich um einen ersten Entwurf handelt. Ich hatte ja keine Ahnung ob das alles am Ende auch so passt wie ich mir das gedacht habe.

In der Nähe von mir wibt die Schiller-Bibliothek damit, dass sie einen Makerspace hat, also bin ich dahin um dort vielleicht zu drucken. Leider versteht man in der Bibliothek etwas anderes unter Makerspace. Dort wird die Möglichkeit geboten einmal im Monat ein Objekt von thingiverse zu laden und zu drucken, vorausgesetzt es dauert nicht zu lange. Wenn die beantragten Fördermittel bewilligt werden, soll das auf einmal die Woche erhöht werden. Das bietet eine schöne Gelegenheit sich mal 3D-Druck anzuschauen, ist aber keine Lösung für mich.

Auf der Suche nach “richtigen” Makerspaces, nach dem Prinzip der offenen Werkstatt, bin ich auf das HappyLab gestossen. Die haben einen Industrie 3D-Drucker, der wäre im Prinzip schon gut gewesen, leider wollen sie 0,45€ pro cm³ Druck- bzw. Stützmaterial. Ich verwende dünnere Wände als sie minimal empfehlen und würde dort dennoch etwa 40€ zahlen.

Am Ende bot mir ein Freund an, dass am Wochenende bei ihm auf der Arbeit zu drucken. Der Chef hatte sich vor einer Weile einen Creality3D CR-10 S5 zugelegt und meinte das wäre ok wenn wir den am Wochenende mal benutzen. Der erste Versuch lief leider schief, irgendwann nach zwei oder drei Lagen stockte der Filamentnachschub und der Drucker arbeitete fleissig in der Luft weiter. Als wir das bemerkten war es bereits zu spät.


printing...

Beim zweiten Versuch haben wir den Deckel des Gehäuses fertig gedruckt, dass hat sehr viel länger gedauert als ich dachte. Wir haben auf anraten des Chefs mit weniger als 50% Druckgeschwindikeit gedruckt. Die Ecken des Deckels haben sich um einen Millimeter von der Glasplatte gelöst und angehoben, dass sieht man auch wenn man genau hinschaut, aber stört mich nicht sonderlich. Das Unterteil des Gehäuses haben wir dann über Nacht drucken lassen mit 35% Druckgeschwindigkeit. Das Ergebnis ist gut. Leider sind auch bei der niedriegen Druckgeschwindigkeit die Ecken etwas hochgekommen.

fertiges Gehäuse

Die Montage

Der Lüfter wurde, zwecks Geräuschminimierung, mit Entkoppel-Gummies im Deckel befestigt. Die Gummidämpfer habe ich mit einem Tropfen Sekundenkleber in den Aussparungen im Deckel geklebt, damit bleiben sie dehnbar genug um den Lüfter wechseln zu können.

Um das Stromversorgungsproblem zu beheben, habe ich einen StepDown-Wandler verwendet. Die hier verwendete QC3-StepDown-Wandler ist im Grunde der Kern eines USB-Ladegeräts. Den Wandler betreibe ich mit einem normalen 12V Netzteil.


12V zu 5V PSU

Im Grunde kann der Wandler viel mehr, er beherrscht QuickCharge3 und kann bis zu 12V und 24W liefern. Zudem kostet er nur etwas über 1€. Die für mich wichtigste Funktion ist, dass er nachregelt, d.h. wenn der Pi wirklich mal Strom zieht und die Spannung runtergeht, erhöht der Wandler automatisch die Spannung um über 5V zu bleiben. Der Plan war den StepDown-Wandler mittels super kurzem Kabel anzuschließen. Leider habe ich kein passendes USB-zu-MicroUSB Kabel gefunden. Alle Kabel waren zu lang oder zu unflexibel. Ich habe mich also enschlossen den Pi direkt über seine PINs mit Strom zu versorgen. Damit umgeht man die Sicherungen des Raspberry. Man muss also sehr aufpassen, vertut man sich hier mit der PIN-Belegung ist der Pi recht schnell hin.

Schließlich muss noch der Lüfter mit 12V versorgt werden und der PWM- und der Tacho-Pin mit dem Raspberry verbunden werden.

Die Lüfterbeschaltung

Der Raspberry Pi kann max. 3.3V an seinen PINs ab, mehr zerstört ihn sehr schnell. Beim Tacho- und PWM-Pin des Lüfters kann es sein, je nach Lüfter-Bauart, dass dort kurzzeitig mehr als 3.3V anliegen. Also habe ich mir eine kleine Schutzschaltung aus dem Internet gesucht, die den Pi vor zu hohen Strömen schützt.


Schutzschaltung

Mein erster Versuch war es, dies mit in die Verkabelung einzubauen. Das hat nicht so gut funktioniert. Ich habe die Schaltung dann einfach auf einer Lochstreifenraster-Platine aufgebaut und mit Schrumpfschlauch isoliert. Die zusätzliche kleine Platine im Gehäuse unterzubringen war eine Herausvorderung. Sie klemmt jetzt auf der Oberseite vom MicroSD Karten Slot, zwischen Pi und Gehäusewand. Das hält erstaunlich gut.

Inbetriebnahme

An der Wand befestigt fand ich den Deckel etwas locker. Laut 3D-Model sind etwa 0,2mm Platz zwischen Deckel und Gehäuse. Ich habe einfach ein paar Lagen Tesafilm auf die Gehäuse Kanten geklebt, der Deckel verdeckt diese und durch die Extralagen sitzt der Deckel nun schön fest.

case in action

Die Steuerung

Ziel war es, dass der Pi aktiv gekühlt wird, wenn dieser Kühlung benötigt. Im normalen Betrieb kann der Lüfter so langsam drehen wie möglich, wenn technisch möglich sogar stehen bleiben. Die Frage war nun, wie aktiv soll die Temperaturüberwachung sein.

Meine erste Idee war ein Skript, welchen per Cronjob jede Minute aufgerufen wird. Nach meinen Beobachtungen steigt und fällt die Temperatur der CPU aber sehr viel schneller. Unter Last dauert es nur Sekunden bis der Pi in den Bereich kommt, in dem er ohne Kühlung drosseln würde. Ein Minuten-Interval ist einfach zu langsam.

Ideal wäre eine Regelung, welche direkt bei einer Temperaturänderung anlaufen würde. Meine Lösung ist nicht ganz so perfekt, sollte aber auch gut genug funktionieren. Ich lese in einem einstellbaren Interval (zur Zeit 10s) die CPU-Temperatur aus und passe danach die Lüfterdrehzahl an.

Um aus dem Tachosignal eine Drehzahl zu bekommen gibt es verschiedene Methoden. Das Tachosignal liefert pro Umdrehung des Lüfters zwei Impulse. Ich habe mehrere Methoden ausprobiert, um aus dem Tachosignal eine Umdrehungszahl zu berechnen, keine funktionierte so wie ich mir das vorstellte.

Die besten Ergebnisse erzielte ich, wenn ich die Dauer von mehreren fallenden Flanken des Tachosignals messe und dann über durchschnittliche Dauer zwischen zwei Impulsen die Drehzahl errechne. Das liefert gute Ergebnisse, solange man nicht während der Beschleunigung misst.


Temperatur-Drehzahl-Kurve

Ich habe zwei Temperaturpunkte definiert, bis zum Punkt A soll der Lüfter mit minimaler Drehzahl laufen, ab dann soll der Lüfter bis zum Punkt B beschleunigen. Über Punkt B soll der Lüfter mit voller Drehzahl laufen.

Monitoring

Die CPU-Temperatur wird schon an mein Monitoring übertragen. Die Lüfterdrehzahl möchte ich auch gern aufzeichen. Problematisch ist eine Drehzahlmessung während der Regelung, da diese oft unsinnige Drehzahlen liefert

#!/usr/bin/python3 -u

import RPi.GPIO as gpio
import wiringpi, time

pin_pwm = 18
wiringpi.wiringPiSetupGpio()
wiringpi.pinMode(pin_pwm, 2) # hardware pwm mode

pin_tacho_bcm = 23
gpio.setmode(gpio.BCM)
gpio.setup(pin_tacho_bcm, gpio.IN)

wait_seconds_before_next_loop = 5
wait_seconds_after_pwm_change_before_measure_rpm = 10
temp_rpm_start_raise = 45
temp_rpm_full_speed = 67
last_temperature = temp_rpm_start_raise

if __name__ == "__main__":
    while True:
        # measure: rotation per minute
        count_cycles_for_average = 10
        gpio.wait_for_edge(pin_tacho_bcm, gpio.FALLING) # wait till falling edge
        start = time.time()                             # start stopwatch
        for impulse_count in range(count_cycles_for_average):
            gpio.wait_for_edge(pin_tacho_bcm, gpio.FALLING)
        duration = time.time() - start                  # stop stopwatch
        frequency = count_cycles_for_average / duration # Hz
        rpm = int(frequency*3) * 10                     # rpm rounded by 10

        # get temperature
        temperature_file = open('/sys/class/thermal/thermal_zone0/temp')
        cur_temp = float(temperature_file.read()) / 1000

        # if current temperature differs last temperatur
        new_pwm = 0
        if cur_temp != last_temperature:
            # calculate new pwm value
            temp_span = temp_rpm_full_speed - temp_rpm_start_raise
            pwm_per_degree = (1024 / temp_span)
            new_pwm = (cur_temp - temp_rpm_start_raise + 1) * pwm_per_degree
            new_pwm = int(0 if new_pwm < 0 else 1024 if new_pwm > 1024 else new_pwm)
            # set new pwm value
            try:
                wiringpi.pwmWrite(pin_pwm, int(new_pwm))
            except:
                rpm = 0
        else:
            last_temperature = cur_temp

        # write rpm to ramdisk file
        with open("/run/user/0/rpm",'w') as out_file:
            out_file.write(str(rpm))
            out_file.close()

        # wait
        wait_seconds = max(wait_seconds_before_next_loop, wait_seconds_after_pwm_change_before_measure_rpm)
        time.sleep(wait_seconds)

Der Python-Server-Daemon selbst weiß natürlich wann er den PWM-Wert ändert, kennt also auch den besten Zeitpunkt zum Messen. Meine Lösung besteht nun darin die Drehzahl im Regelprozess zu messen und in eine Datei auf einer RAM-Disk zu schreiben. Die Drehzahldatei wird mehrmals die Minute aktuallisiert, um damit die MicroSD-Karte nicht kaputt zu schreiben, verwende ich die RAM-Disk, welche das Debiansystem schon angelegt hat.

Zum starten und stoppen bzw. überwachen des Python-Server-Daemon definiere ich ihn als systemd-Dienst. Das Skript liegt hier:

/etc/systemd/system/fancontrol.service

Aktiviert und gestartet wird es wie folgt:

systemctl enable pyserver
systemctl start pyserver
[Unit]
Description=Fan control server
After=syslog.target

[Service]
Type=simple
WorkingDirectory=/etc/check_mk
ExecStart=/etc/check_mk/fancontrol_server.py
SyslogIdentifier=fancontrol
StandardOutput=syslog
StandardError=syslog
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

Das Skript, welches dann die Drehzahl ausliest und ans Monitoring überträgt kann somit los gelöst von der Regelung ausgeführt werden.

#!/usr/bin/python3

import sys, os, time, stat

FILE = "/run/user/0/rpm"
WARN_FILE_AGE_IN_SECONDS = 60
CRIT_FILE_AGE_IN_SECONDS = 120

if not os.path.exists(FILE):
  print("UNKNOWN - FanControl file not found.")
  sys.exit(3)

file_age = time.time() - os.stat(FILE)[stat.ST_MTIME]
if file_age > WARN_FILE_AGE_IN_SECONDS:
  print("WARNING - FanControl RPM Value is old.")
  sys.exit(1)

if file_age > CRIT_FILE_AGE_IN_SECONDS:
  print("CRITICAL - FanControl RPM Value is outdated.")
  sys.exit(2)

line = ""
with open(FILE, 'r') as infile:
    try:
        data = infile.read()
        infile.close()
    except :
         print("UNKNOWN - FanControl file open failed.")
         sys.exit(3)

rpm = int(data)
if rpm < 1:
    print("CRITICAL - FanControl RPM is 0..")
    sys.exit(2)

if rpm < 500:
    print("WARNING - FanControl RPM is too slow.")
    sys.exit(1)

# maybe waring/critical if too long too high

print("OK - FanControl is working. Fan has " +str(rpm)+ "rpm. |fan="+str(rpm)+";:500;:200;600;3500")
sys.exit(0)

So sieht das dann in meinem Monitoring aus, ich habe hier mal Last aufs System gegeben, damit man den Peak am Ende des Graphen sehen kann.

Temperatur-Drehzahl

Fazit und Verbesserungen

So im Ganzen finde ich es ein gelungenes Projekt. Hier ein paar Sachen die ich beim nächsten mal besser machen würde:

  • Das Gehäuse etwas größer machen, damit Platz ist für die Schutzschaltung.
  • Eine bessere Wandbefestigung, zur Zeit muss man den Pi dafür ausbauen.
  • Den Pi dichter an die Gehäusewand, manche USB Stecker passen z.Z. nicht.
  • Ein Kabel finden und verwenden, dass zwischen StepDown-Wandler und Pi passt.
  • Gehäusewand etwas dicker machen, bringt mehr Stabilität.