+++ Lese-Modus aktiviert +++

Mein Semesterferien-Projekt: der Stickformatter

Wir teilen uns Werkzeug, Räume und Kaffeemaschinen. Warum also nicht auch USB Sticks und Speicherkarten? An der Uni oder in der Werkstatt greift man schnell nach dem nächsten freien Stick. Den Kommilitonen und Kollegen vertraut man natürlich. Aber was passiert, wenn unbemerkt ein infizierter Stick dazwischen landet? Ein einziges präpariertes Speichermedium wandert von Rechner zu Rechner und im schlimmsten Fall ist direkt das gesamte Netzwerk betroffen. Aus Sicht der IT Sicherheit ein riesiger blinder Fleck. Genau das hat mich auf die Idee für mein Semesterferien Projekt gebracht: Ein automatischer Stickformater.

Dieses Projekt eignet sich gut, wenn du gerade im Studium bist und für eine Vorlesung ein eigenes IoT-Projekt umsetzen muss. Aber besprich dies davor mit deinem Dozenten/Professor, wie viel du aus dem Internet übernehmen darfst und wie viel Eigenleistung vorgeschrieben ist.

Was macht der Stickformatter? Er formatiert Sticks!

Im Gerät drin steckt ein kleiner Raspberry Pi, dem ich ein paar weitere Komponenten angeschlossen und eine passende Hülle verpasst habe. Es gibt zwei Wege, wie man mit dem Gerät kommunizieren kann: entweder direkt über die zwei LEDs an der Oberseite oder über eine Web UI.

Die Nutzung funktioniert dabei ganz einfach: Wir stecken das Gerät an den Strom und warten kurz, bis die grüne LED leuchtet. Schließlich können wir einen USB Stick anstecken. Die rote LED blinkt dann ein paar Sekunden, während der Stick formatiert wird, und am Ende leuchtet wieder die grüne LED. Das heißt, der Stick ist fertig formatiert und kann direkt wieder sorglos benutzt werden. In der Web UI können wir ebenfalls den aktuellen Status sehen und auch den Verlauf der letzten Durchläufe nachverfolgen.

Der Stickformatter von Innen: der Raspberry Pi Zero.

Aber von vorn.

Ich habe im Juli meinen Bachelor erfolgreich abgeschlossen (yay) und musste bis zum Start meines Masterstudiums Ende September warten. Das bedeutete erst einmal sehr viel Freizeit. Klar, am Anfang entspannt man, macht Urlaub und genießt die Ruhe. Aber irgendwann kommt der Punkt, den viele kennen: Man möchte wieder etwas anpacken und produktiv sein.

Eigentlich hatte ich zu dieser Zeit schon ein Projekt im nxtspace Heilbronn am Laufen. Einen automatischen Meditationshelfer mit einem kleinen 3,5 Zoll Screen, einem Raspberry Pi und einer gedruckten Hülle. Doch irgendwie war die Motivation weg. Das Projekt hat mich nicht mehr richtig überzeugt, vermutlich weil mir das klare Ziel gefehlt hat.

Deshalb habe ich mich mit meinem zugeteilten Mentor zusammengesetzt, das alte Projekt kurzerhand beendet und neu überlegt. Denn eines war mir klar: Ich wollte in dieser Zeit unbedingt praktische Skills wie 3D Druck und Löten lernen. Das sind Themen, die im klassischen IT Studium kaum vorkommen, die ich persönlich für die Zukunft aber als extrem wertvoll einschätze.

Was ist das nxtspace?

Das nxtspace in Heilbronn (https://www.nxtspace.de/) wird von CosmosHN betrieben. Es ist ein Space, an dem Studierende der Heilbronner Institutionen ihre Ideen anmelden und daran arbeiten können. Das muss nicht unbedingt eine Startup Idee sein, sondern kann auch ein privates Alltagsproblem lösen. Unter anderem stehen dort 3D Drucker und Lötstationen zur Verfügung, um eigene Ideen in die Tat umzusetzen.

Wie startet man also am besten ein neues Projekt? Ganz einfach: Man schaut sich um. Wo gibt es Optimierungspotenzial oder Risiken, die so alltäglich geworden sind, dass man sie gar nicht mehr wahrnimmt? Da ist mir der Ablauf bei den 3D Druckern im nxtspace ins Auge gefallen. Der Upload der Druckdateien funktioniert dort über USB Sticks, die offen herumliegen und von allen genutzt werden. Da hat es Klick gemacht: Das lässt sich doch lösen!

Die benötigten Komponenten

KomponenteAnzahl
Raspberry Pi Zero W1
Micro SD Karte (mindestens 16 GB)1
SD Karte SPI Modul (nicht Micro SD)1
Kabel USB Typ C auf Micro USB Typ B1
Buchsenleiste für Raspberry Pi1
Jumper Wire Female to Female10
USB-OTG-Adapter1
330 Ω Widerstand2
LEDs2

Die benötigten Komponenten kostenunter 50 Euro (wenn man einen originalen Raspberry Pi nimmt). Wenn man auf einen Raspberry Pi verzichtet und dafür günstige Alternativen nimmt, können die kosten Locker auf unter 30 Euro gedrückt werden. Und ach ja, du brauchst einen 3D-Drucker und im besten Fall eine Lötstation

Die Software (C++)

Bevor es an das Gehäuse geht, müssen wir testen, ob die Software läuft und der Raspberry Pi Zero überhaupt mitmacht.

Als Betriebssystem habe ich Raspberry Pi OS Lite gewählt, das auf Debian basiert. Schwerere Systeme wie Ubuntu kann man auf dem Pi Zero nämlich vergessen. Schließlich arbeiten wir hier mit einem Single Core 1 GHz Prozessor und gerade einmal 512 MB RAM. Zum Vergleich: Ein Raspberry Pi 4B hat einen Quad Core 1,5 GHz Prozessor.

Über den Raspberry Pi Imager lassen sich WLAN und SSH Schlüssel glücklicherweise direkt vor dem ersten Start konfigurieren. Karte flashen, in den Pi stecken, Strom dran und per OTG Adapter den USB Stick verbinden. Wichtig: Bitte nutzt einen Teststick ohne wertvolle Daten!

Nun loggen wir uns über ein Terminal im selben Netzwerk ein:

ssh (dein nutzername)@(name des pis)

Falls die Verbindung nicht klappt: Einfach kurz warten, bis der Pi fertig gebootet hat, oder das LLM eures Vertrauens fragen.

Sobald die Verbindung steht, geht es an das Programm. Ich habe mich dafür entschieden, den Formatter in C++ zu schreiben. Warum C++ und kein Java oder Python? Ganz einfach: Ich wollte jeden Overhead vermeiden. C++ arbeitet direkt nah an der Hardware und ist auf der schwachen Rechenleistung die mit Abstand schnellste Lösung. Und machen wir uns nichts vor: Für den Prototyp kann man das wunderbar vibecoden und alles in eine Datei packen. Sauber getrennte Module wären schöner, aber das ist ein Problem für die Zukunft.

Wir installieren zuerst die benötigten Bibliotheken für SQLite und GPIO Steuerung sowie die Mongoose Webserver Bibliothek:

sudo apt update
sudo apt install libsqlite3-dev g++ libgpiod-dev wget -y
wget https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c
wget https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.h
nano usb_formatter.cpp

Du kannst die Befehle einfach kopieren und einfügen. Damit Installierst du alle Packages, die du für die Software brauchst und wir haben direkt eine neue Datei erstellt, wo wir den Code einfügen. Hierbei haben wir alles in einer Datei. Klar, ich weiß, eine Auftrennung nach Funktionalitäten wäre vielleicht sinnvoller, aber das lassen wir ein Problem für die Zukunft sein. Schließlich kopierst du folgenden Code und fügst ihn ein:

#include "mongoose.h"
#include <iostream>
#include <chrono>
#include <thread>
#include <atomic>
#include <string>
#include <filesystem>
#include <gpiod.hpp>
#include <cstdlib>
#include <cstdio>
#include <array>
#include <sqlite3.h>
#include <mutex>

namespace fs = std::filesystem;

std::mutex statusMutex;
std::string currentStatus = "Warte auf Speichermedium...";

void setStatus(const std::string& msg) {
    std::lock_guard<std::mutex> lock(statusMutex);
    currentStatus = msg;
    std::cout << msg << std::endl;
}

std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    int (*pclose_ptr)(FILE*) = pclose;
    std::unique_ptr<FILE, int(*)(FILE*)> pipe(popen(cmd, "r"), pclose_ptr);
    if (!pipe) return "";
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    if (!result.empty() && result.back() == '\n') result.pop_back();
    return result;
}

void logToDatabase(long long duration, const std::string& usedSpace, bool formatSuccess, bool alreadyFormatted) {
    sqlite3* db;
    if (sqlite3_open("speicherlog.db", &db) == SQLITE_OK) {
        std::string sqlCreateTable = "CREATE TABLE IF NOT EXISTS logs ("
            "id INTEGER PRIMARY KEY AUTOINCREMENT,"
            "eingesteckt_um DATETIME DEFAULT (datetime('now', 'localtime')),"
            "dauer_ms INTEGER,"
            "belegter_platz TEXT,"
            "formatierung_erfolgreich BOOLEAN,"
            "war_schon_formatiert BOOLEAN);";
        sqlite3_exec(db, sqlCreateTable.c_str(), 0, 0, 0);

        std::string sqlInsert = "INSERT INTO logs (dauer_ms, belegter_platz, formatierung_erfolgreich, war_schon_formatiert) VALUES ("
            + std::to_string(duration) + ", '"
            + usedSpace + "', "
            + (formatSuccess ? "1" : "0") + ", "
            + (alreadyFormatted ? "1" : "0") + ");";
        sqlite3_exec(db, sqlInsert.c_str(), 0, 0, 0);
        sqlite3_close(db);
    }
}

std::string getLogsJson() {
    sqlite3* db;
    std::string json = "[";
    if (sqlite3_open("speicherlog.db", &db) == SQLITE_OK) {
        sqlite3_stmt* stmt;
        const char* sql = "SELECT * FROM logs ORDER BY id DESC LIMIT 50;";
        if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) == SQLITE_OK) {
            bool first = true;
            while (sqlite3_step(stmt) == SQLITE_ROW) {
                if (!first) json += ",";
                json += "{";
                json += "\"id\":" + std::to_string(sqlite3_column_int(stmt, 0)) + ",";
                json += "\"eingesteckt_um\":\"" + std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1))) + "\",";
                json += "\"dauer_ms\":" + std::to_string(sqlite3_column_int(stmt, 2)) + ",";
                json += "\"belegter_platz\":\"" + std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3))) + "\",";
                json += "\"formatierung_erfolgreich\":" + std::string(sqlite3_column_int(stmt, 4) ? "true" : "false") + ",";
                json += "\"war_schon_formatiert\":" + std::string(sqlite3_column_int(stmt, 5) ? "true" : "false");
                json += "}";
                first = false;
            }
        }
        sqlite3_finalize(stmt);
        sqlite3_close(db);
    }
    json += "]";
    return json;
}

const std::string indexHtml = R"=====(
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Pi Laufwerk Interface</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 30px; background: #f4f4f9; }
        h1 { color: #333; }
        #statusBox { padding: 15px; background: #e3f2fd; border-left: 5px solid #2196f3; font-size: 1.2em; margin-bottom: 20px; border-radius: 4px; }
        table { width: 100%; border-collapse: collapse; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
        th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
        th { background: #4CAF50; color: white; }
        tr:nth-child(even) { background-color: #fafafa; }
    </style>
</head>
<body>
    <h1>Pi Laufwerk Interface</h1>
    <div id="statusBox">Status: Lade...</div>
    <table>
        <thead>
            <tr>
                <th>ID</th><th>Zeitpunkt</th><th>Dauer (ms)</th><th>Belegter Platz</th><th>Erfolgreich</th><th>Bereits formatiert</th>
            </tr>
        </thead>
        <tbody id="logsTable">
        </tbody>
    </table>
    <script>
        async function fetchData() {
            try {
                let resStatus = await fetch('/api/status');
                let jsonStatus = await resStatus.json();
                document.getElementById('statusBox').innerText = 'Status: ' + jsonStatus.status;

                let resLogs = await fetch('/api/logs');
                let jsonLogs = await resLogs.json();
                let html = '';
                jsonLogs.forEach(log => {
                    html += `<tr>
                        <td>${log.id}</td>
                        <td>${log.eingesteckt_um}</td>
                        <td>${log.dauer_ms}</td>
                        <td>${log.belegter_platz} Bytes</td>
                        <td>${log.formatierung_erfolgreich ? 'Ja' : 'Nein'}</td>
                        <td>${log.war_schon_formatiert ? 'Ja' : 'Nein'}</td>
                    </tr>`;
                });
                document.getElementById('logsTable').innerHTML = html;
            } catch (e) {
                console.error(e);
            }
        }
        setInterval(fetchData, 1500);
        fetchData();
    </script>
</body>
</html>
)=====";

static void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
    if (ev == MG_EV_HTTP_MSG) {
        struct mg_http_message *hm = (struct mg_http_message *) ev_data;
        if (mg_match(hm->uri, mg_str("/api/status"), NULL)) {
            std::string statusStr;
            {
                std::lock_guard<std::mutex> lock(statusMutex);
                statusStr = currentStatus;
            }
            std::string json = "{\"status\":\"" + statusStr + "\"}";
            mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s", json.c_str());
        } else if (mg_match(hm->uri, mg_str("/api/logs"), NULL)) {
            std::string json = getLogsJson();
            mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s", json.c_str());
        } else {
            mg_http_reply(c, 200, "Content-Type: text/html; charset=utf-8\r\n", "%s", indexHtml.c_str());
        }
    }
}

void webServerThread() {
    struct mg_mgr mgr;
    mg_mgr_init(&mgr);
    mg_http_listen(&mgr, "http://0.0.0.0:3333", ev_handler, NULL);
    while (true) {
        mg_mgr_poll(&mgr, 1000);
    }
    mg_mgr_free(&mgr);
}

int main() {
    std::thread serverThread(webServerThread);
    serverThread.detach();

    const gpiod::line::offset PIN_GREEN = 4;
    const gpiod::line::offset PIN_RED = 5;

    gpiod::chip chip("/dev/gpiochip4");
    gpiod::line_settings settings;
    settings.set_direction(gpiod::line::direction::OUTPUT);
    
    gpiod::line_config line_cfg;
    line_cfg.add_line_settings({PIN_RED, PIN_GREEN}, settings);

    auto request = chip.prepare_request()
                       .set_consumer("formatter")
                       .set_line_config(line_cfg)
                       .do_request();

    auto setRed = [&](bool on) { request.set_value(PIN_RED, on ? gpiod::line::value::ACTIVE : gpiod::line::value::INACTIVE); };
    auto setGreen = [&](bool on) { request.set_value(PIN_GREEN, on ? gpiod::line::value::ACTIVE : gpiod::line::value::INACTIVE); };

    while (true) {
        setStatus("Bereit und warte auf Speichermedium...");
        setRed(false);
        setGreen(true);
        std::this_thread::sleep_for(std::chrono::seconds(2));

        std::string usbDrive = exec("lsblk -d -n -o NAME,TRAN | awk '$2==\"usb\" {print \"/dev/\"$1}' | head -n 1");

        if (!usbDrive.empty()) {
            auto startTime = std::chrono::steady_clock::now();
            setStatus("Speichermedium erkannt: " + usbDrive);
            
            setGreen(false);
            
            std::atomic<bool> isProcessing(true);
            std::thread blinkThread([&]() {
                while (isProcessing) {
                    setRed(true);  std::this_thread::sleep_for(std::chrono::milliseconds(250));
                    setRed(false); std::this_thread::sleep_for(std::chrono::milliseconds(250));
                }
            });

            std::string partition = usbDrive + "1";
            bool needsFormat = true;
            bool formatSuccess = true;
            std::string usedSpace = "unbekannt";
            std::string fsType = exec(("lsblk -n -o FSTYPE " + partition).c_str());

            if (fsType == "vfat") {
                exec("mkdir -p /mnt/usbstick");
                if (system(("mount " + partition + " /mnt/usbstick 2>/dev/null").c_str()) == 0) {
                    usedSpace = exec("df -B1 /mnt/usbstick | awk 'NR==2 {print $3}'");
                    if (usedSpace.empty()) usedSpace = "0";

                    bool isEmpty = true;
                    for (const auto& entry : fs::directory_iterator("/mnt/usbstick")) {
                        std::string filename = entry.path().filename().string();
                        if (filename != "System Volume Information" && filename != "FOUND.000") {
                            isEmpty = false;
                            break;
                        }
                    }
                    exec("umount /mnt/usbstick");
                    
                    if (isEmpty) {
                        setStatus("Medium ist bereits passend formatiert und leer.");
                        needsFormat = false;
                    }
                }
            }

            if (needsFormat) {
                setStatus("Formatiere Speichermedium...");
                system(("umount " + usbDrive + "* 2>/dev/null").c_str());
                system(("wipefs -a " + usbDrive + " >/dev/null 2>&1").c_str());
                system(("parted -s " + usbDrive + " mklabel msdos").c_str());
                system(("parted -s " + usbDrive + " mkpart primary fat32 1MiB 100%").c_str());
                
                int result = system(("mkfs.fat -F 32 " + partition).c_str());
                formatSuccess = (result == 0);
            }

            isProcessing = false;
            blinkThread.join();
            
            auto endTime = std::chrono::steady_clock::now();
            auto durationMs = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();

            logToDatabase(durationMs, usedSpace, formatSuccess, !needsFormat);
            
            setRed(false);
            setGreen(true);

            setStatus("Vorgang beendet. Bitte das Speichermedium entfernen.");
            while (true) {
                std::string checkDrive = exec(("lsblk -d -n -o NAME | grep " + usbDrive.substr(5)).c_str());
                if (checkDrive.empty()) {
                    setStatus("Speichermedium wurde erfolgreich entfernt.");
                    std::this_thread::sleep_for(std::chrono::seconds(1));
                    break; 
                }
                std::this_thread::sleep_for(std::chrono::seconds(2));
            }
        }
    }
    return 0;
}

Nach dem Einfügen speichern wir die Datei mit STRG + X und drücken ENTER.

Jetzt kompilieren wir das Projekt und starten das Programm:

g++ -std=c++17 usb_formatter.cpp mongoose.c -o usb_formatter -lgpiodcxx -lsqlite3 -pthread
sudo ./usb_formatter

Schon läuft die Software! Unter http://[name_des_pis]:3333 lässt sich das Web Interface direkt im Browser aufrufen, sobald man im selben Netzwerk ist.

Wichtig: In dem Tutorial werden wir nicht den SD Karten Slot konfigurieren, das mache ich noch in der Zukunft!

Das Case

Das war für mich der spannendste Teil des ganzen Projekts. In diesen Semesterferien habe ich Fusion360 zum ersten Mal angefasst. Ich hatte zwar vorher schon mit Blender und Unity experimentiert, aber da ging es nie um solche Präzision. Hier muss wirklich alles auf den Millimeter genau stimmen. Der Raspberry Pi und alle anderen Komponenten müssen exakt reinpassen und das Gehäuse soll komplett ohne Schrauben auskommen, also über einen Snap Fit schließen.

Wie ich jedes Detail genau modelliert habe, würde den Rahmen sprengen. Stattdessen stelle ich euch einfach die STL Dateien zur Verfügung.

Das Case in Fusion360

Was man dem fertigen Ergebnis nicht ansieht: Das ist bereits die vierte Version des Gehäuses. Ich habe immer wieder modelliert, gedruckt und nachkorrigiert, bis alles perfekt gesessen hat. Bei der ersten Version hat zum Beispiel überhaupt nichts gepasst. Ich konnte den Raspberry Pi nicht einmal einsetzen, weil die Pins für die Halterung viel zu weit auseinander lagen.

Die gedruckten Cases, bevor es perfekt gepasst hat.

Hier der zip-Ordner mit dem oberen und unteren Teil des Cases:

Löten

Bevor es losgeht, benötigst du eine Lötstation, einen Seitenschneider und eine sogenannte dritte Hand, also eine kleine Halterung mit Klammern, die deine Bauteile festhält.

Falls du noch nie gelötet hast: Keine Panik. Schau dir ein paar kostenlose YouTube Tutorials an, es ist wirklich einfacher als es aussieht. Ich bin selbst wahrlich kein Profi und habe für dieses Projekt das erste Mal seit neun Jahren wieder gelötet. Man sieht den Lötstellen zwar an, dass ich kein Profi bin, aber es funktioniert, und das ist das Wichtigste!

Mein wichtigster Rat vorab: Fange unbedingt zuerst mit den LEDs an und löte erst danach den Raspberry Pi. Wenn du eine LED verbrennst, verlierst du 10 Cent. Beim Raspberry Pi sind direkt 25 Euro hin.

Zwei wichtige Dinge solltest du beachten, damit du nicht dieselben Fehler machst wie ich.

Erstens: Nimm dir Zeit für jeden einzelnen Pin. Die Lötstelle muss sauber von Zinn umschlossen sein und festen Kontakt zur Platine haben. Ich war faul und habe zunächst nur zwei Pins festgelötet, damit die Leiste hält. Ich dachte, die restlichen Pins drücken schon fest genug auf die Kontakte. Drei Stunden Fehlersuche später, in denen ich mich gewundert habe, warum die LEDs nicht leuchten, stellte sich genau das als Ursache heraus.

Zweitens: Falls du eine farbig codierte Buchsenleiste verwendest, achte unbedingt auf die Ausrichtung, bevor du lötest. Ich habe meine falsch herum angelötet. Damit war die visuelle Orientierung durch die bunten Farben komplett für die Katz.

Die Lötstellen an der Unterseite des Raspberry Pis.

Die LEDs können wir nicht direkt an den Raspberry Pi anschließen. Wenn sie ohne Vorwiderstand betrieben werden, fließt viel zu viel Strom und die Dioden brennen nach kurzer Zeit durch. Eigentlich nutzt man für so etwas ein kleines Breadboard, aber das passt beim besten Willen nicht in unser Gehäuse. Also habe ich mich für gepflegten Eigenpfusch entschieden und alles frei Schnauze direkt zusammengelötet. Wenn du vorhast, so ein Gerät irgendwann einmal zu verkaufen: Mache es bitte niemals so! Für unseren Prototyp reicht diese Wildwest Methode aber vollkommen aus.

Kürze am besten zuerst die Beinchen der LED und des Widerstands mit dem Seitenschneider ein. Das spart wertvollen Platz im Gehäuse und sorgt für deutlich stabilere Lötstellen. Achte beim Löten unbedingt auf die Polung der LED. Das längere Beinchen ist der Pluspol, an den auch der Widerstand gelötet wird.

Für die Verkabelung nutzt du am besten ein Male to Female Kabel für den Pluspol und ein Female to Female Kabel für Minus. Beim Minusanschluss entfernst du einfach das schwarze Plastikgehäuse an einer Seite, isolierst den Draht ein kleines Stück ab und lötest diesen blanken Draht direkt an das kurze Beinchen der LED. An das verbliebene freie Ende des Widerstands wird dann die abgeknipste Seite des Male to Female Kabels gelötet.

Falls du Schrumpfschlauch daheim hast, also diesen Kunststoff den man drübermacht und erhitzt damit er sich zusammenzieht: Benutze ihn unbedingt! Damit isolierst du die Stellen sauber, verhinderst Kurzschlüsse und der Pfusch sieht am Ende sogar fast professionell aus.

Ein wichtiger Tipp noch für die Kabelwahl: Nimm am besten rot für Plus sowie grün oder schwarz für Minus. Wenn du einfach irgendwelche bunten Restkabel nimmst, verlierst du beim späteren Anstecken an den Pi schnell den Überblick darüber, wo welcher Strom fließt.

Das Finale: Alles miteinander verbinden

Nun haben wir alles. Jetzt heißt es, alles miteinander verbinden. Als eins machen wir die LEDs, vielleicht hast du schon an dem gedruckten deckel von deinem case zwei löcher festgestellt, durch diese muss die LED durch. Welche du wo durchmachst ist egal. Das kannst du selbst entscheiden, auf die funktionalität hat es keinen Einfluss. Schließlich verbinden wir die LEDs mit den richtigen Pins. Als Grundlage habe ich hierfür die Folgende Grafik von AZ-Delivery genommen, die PINS sind schön durchnummeriert und deswegen können wir es uns einfacher machen:

Quelle: https://www.az-delivery.de/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/ethernet-fur-den-raspberry-pi-zero-enc28j60?srsltid=AU7gw4WbSg-0EdF3Zj_Fx79GVPITtGdk5hBh6mtV05iao8DTNKTsMY93

Obwohl wir den SD-Karten Slot in diesem Tutorial nicht aktivieren, schließen wir vorläufig schonmal auch alles richtig an.

AnschlussPin am Raspberry Pi
Rote LED Eingang (rot)Pin 29 (GPIO 5)
Rote LED Ausgang (grün/schwarz)Pin 30 (GND)
Grüne LED Eingang (rot)Pin 7 (GPIO 4)
Grüne LED Ausgang (grün/schwarz)Pin 6 (GND)
SD Slot VCC (Stromversorgung 5V)Pin 2 (+5V)
SD Slot GND (Masse)Pin 9 (GND)
SD Slot CS (Chip Select)Pin 24 (GPIO 8 / CE0)
SD Slot MOSI (Master Out Slave In)Pin 19 (GPIO 10 / MOSI)
SD Slot MISO (Master In Slave Out)Pin 21 (GPIO 9 / MISO)
SD Slot SCK (Clock / Taktleitung)Pin 23 (GPIO 11 / SCLK)

FAQ

Kurzgefasst: Falls irgendwas nicht klappt, frage eine LLM. Die beantwortet es schneller und genauer als ich.

« Zurück zur Startseite