🎄 Steckbrett-Lagerfeuer

Projekt-Steckbrief

  • Difficulty: Anfänger 1/5
  • Kosten: 8…25€
  • Zeit ~1h

Überblick

Dies ist ein schnelles und einfaches Projekt, das zeigt, wie man mit einem Microcontrollerboard und einigen LEDs eine warme und gemütliche Wohnzimmeratmosphäre schaffen kann. An eine Powerbank angeschlossen, wird diese kleine Bastelei den ganzen Abend leuchten und eine warme und gemütliche Atmosphäre schaffen. Ich habe Teile der Idee für dieses Projekt aus einem Buch entliehen, in dem ich viel gelesen habe, als ich vor Jahren mit dem Entwurf eingebetteter Software in C begann.

Die Projektkosten hängen von den gewählten Komponenten ab. Wenn ein original Arduino verwendet wird, liegen sie am oberen Ende des Bereichs. So kann man den hart arbeitenden Entwicklern und Ingenieuren von Arduino Anerkennung zollen, die Open-Source-Lösungen für uns alle schaffen 🥳.

Die Hardware

Natürlich kann man jedes Mikrocontroller-Board verwenden, das mindestens 8 digitale Ausgänge und 1 analogen Eingang unterstützt. In meinem Beispiel verwende ich ein Arduino Nano (Nachbau), das ich irgendwo herumfliegen hatte. Zeit für ein Upgrade auf das “Original”… Des Weiteren benötigt man ein Steckbrett, ein paar Drähte und acht LEDs. Ich empfehle, 5 warmweiße LEDs, 2 gelbe LEDs und 1 rote LED mit ähnlicher Helligkeit (Lumenzahl) zu verwenden, um Farben zu erhalten, die einem echten Feuer ähnlich sind.

Die Software

Die Software erzeugt einen flackernden Lichteffekt mit unterschiedlicher Geschwindigkeit.

Einrichtung

Beim Start definiere ich in setup() die Pins, an die die LEDs angeschlossen sind, als Ausgänge. Ich lasse einen offenen Analog-Pin einmal auslesen und nehme dessen (undefinierten) Wert als Seed für den später besprochenen Pseudo-Zufallszahlengenerator.

Schleife

In der Endlosschleife berechnet das Programm eine neue Zufallszahl und übergibt diese dann an die nachgeschalteten Funktionen. Hier erzeugt ein Zufallszahlengenerator einen Wert, der dann verwendet wird, um

  1. zu bestimmen, welche der acht LEDs leuchten soll
  2. die Zeit festzulegen, für die dieser LED-Status beibehalten werden soll.

Schließlich werden die LEDs entsprechend gesetzt und die Schleife wiederholt sich am Anfang.

Linear rückgekoppeltes Schieberegister

Der Zufallszahlengenerator ist als sogenanntes linear feedback shift register (LSFR) mit einer Länge von 32bit implementiert (wiederholt sein Muster nach max. 2^32 Schritten). Diese Implementierung verwendet den “Galois-Typ” von LFSRs.

Galois LFSR Implementierung

void loop() 
{
    iRandNum = (iRandNum >> 1) ^ (-(iRandNum & 1u) & 0xd0000001u);
    [...]
}

In dieser Codezeile passiert eine Menge, also schauen wir sie uns mal genauer an.

Wesentlich für diese Art von Zufallszahlengeneratoren ist die XOR-Operation, die durch ein ^ in der Mitte der Codezeile angezeigt wird. XOR” bedeutet “eXclusive OR”, das nur dann eine logische 1 ausgibt, wenn die logischen Eingänge A und B unterschiedlich sind. Wenn “A” und “B” beide “0” oder beide “1” sind, wird dieses Gatter “0” ausgeben.

Beispiel für binäres XOR:

    A         B           Ergebnis
0b00001111 ^ 0b00110011 = 00111100

Links vom XOR-Operator wird die aktuelle Zufallszahl um 1 nach rechts verschoben (>>). Dabei wird ihr gesamter Inhalt wird um ein Bit geschoben. In numerischer Hinsicht bedeutet dies, dass der Wert ganzzahlig durch zwei dividiert wird.

Beispiel für binäres SHIFT:

    A         B   Ergebnis     Als Zahl:
0b00001111 >> 1 = 0b00000111   15 >> 1 = 7

OK, analysieren wir die obige Zeile Code weiter. Direkt neben dem XOR-Operator findet eine weitere Operation statt: Ein logisches AND (&) zwischen unserer aktuellen Zufallszahl und 1u, was “unsigned 1” bedeutet. Dabei werden alle höheren Bits des Zufallszahlengenerators maskiert. Das Ergebnis dieser Aktion ist eine einfache “0”, wenn die Zahl eine “0” als niedrigstwertiges Bit hat usw.

Beispiel für binäres UND:

    A        B   Ergebnis 
0b00001111 & 1 = 0b00000001

Die Negation am Anfang dieses Terms bedeutet, dass, wenn das Ergebnis des vorherigen & 0 war, der gesamte Term wieder 0b00000000... sein wird. Wenn das Ergebnis -1 lautet, sieht der 2er-Komplement-Binärcode stattdessen wie folgt aus: 0b1111111....

Dies ist eine einfache Möglichkeit, einen einzelnen booleschen Wert auf einen beliebigen längeren Datentyp anzuwenden, ohne eine Verzweigungslogik wie bei einer if()-Klausel zu verwenden.

Und schließlich gibt es ein weiteres logisches AND zwischen diesem Begriff und einer festen Bitmaske 0xd0000001u. Letztere sind die “Anzapfungen” des LFSR, d.h. welche Bits des aktuellen Wertes in das System zurückgeführt werden. Die Aufschlüsselung dieses Hex-Wertes in Boolesche Werte zeigt: 0xd = 0b1101”, d.h. die Bits 32, 31, 29 und 1 werden mit der aktuellen Zufallszahl maskiert, so dass nur ihre Werte unberührt bleiben, während alle anderen auf “0” gesetzt werden.

Galois LFSR Zusammenfassung

Puh, das war eine Menge. Es ist Zeit für ein paar Minuten Pause.

Gut, fassen wir die Codezeile oben nochmal zusammen:

iRandNum = (iRandNum >> 1) ^ (-(iRandNum & 1u) & 0xd0000001u);

Bedeutung: “Wenn das niedrigstwertige Bit der aktuellen Zufallszahl Null ist, dann verschiebe die Zufallszahl einfach nach rechts. Andernfalls XOR anwenden mit den abgegriffenen Bits der aktuellen Zufallszahl.”

LFSRs sind weit verbreitet in der drahtlosen Kommunikationstechnik, sie sind interessant für Prüfsummenberechnungen, Kryptographen und können sogar bei Programmierspielen nützlich sein, z.B. bei der Erstellung prozeduraler Karten. LFSRs sind einfach zu implementieren (wie man oben sieht, benötigen sie im Extremfall sogar nur eine einzige Codezeile!) und verbrauchen nicht viele Ressourcen. Hier ist ein hervorragender Blogpost, der sich eingehend mit LFSRs beschäftigt, wenn Sie eher der visuelle Typ sind (und weniger der logische). Nachteilig ist, dass ihre “Zufallsqualität” schlecht ist, weil sie deterministisch sind.

Alles in allem fand ich das Thema so interessant, dass ich mich entschlossen habe, hier einen LFSR manuell zu implementieren, anstatt die Arduino-Funktion random() zu verwenden.

Gif: Pitfall! Game scene
Pitfall! Spielszene: prozedurales Leveldesign mittels LFSR [ 1 ]

Ein Problem bleibt

Nun, wenn Sie die Codezeile vollständig verstanden haben, sehen Sie, was passiert, wenn die Zufallszahl nur Nullen enthält.

Er wird nie wieder aus diesem Zustand herauskommen 💀.

Deshalb sollte man diese Art von LFSR niemals mit “0” initialisieren.

Ansteuerung der LEDs

Der Rest der Software hat viel mehr Zeilen, macht aber auch viel mehr langweilige Sachen. Sie nimmt den generierten 32-Bit-Zufallswert und lässt ihn an den 8 LED-Ausgängen vorbeilaufen, d.h. jede LED “sieht” jedes Bit des Wertes. Zwischen jeder dieser Iterationen wird eine zufällige Verzögerung verwendet, so dass die Flackergeschwindigkeit auf eine Änderungsrate begrenzt ist, die wir Menschen noch wahrnehmen können.

Die Hardware

Image: fireplace breadboard design detail view Zum Glück ist die Hardware einfacher zu erklären als die Software:

  1. Ein Arduino Nano-Board ist mit einem Prototyping-Board verbunden.
  2. Anoden der acht LEDs (+) sind mit den digitalen Ausgängen D2...D9 verbunden.
  3. Ihre Kathoden werden mit Jumpern gebrückt und mit dem “GND” des Arduino verbunden.

Dies ist die einfachste Konstruktion, die mir für diese Aufgabe einfiel.

Verbesserungen

  1. Jede LED sollte einen Vorwiderstand bekommen.
    • In meinem Design ist der Strom nur durch den maximalen Strom begrenzt, den der Arduino Pin treiben kann.
    • Entweder kann die LED deutlich mehr als 40mA Spitzenstrom verkraften oder sie wird unweigerlich irgendwann durchbrennen.
    • Bei der Berechnung des Widerstands sollte man sich an das Datenblatt der LED halten, da verschiedene LED-Farben unterschiedliche Spannungen haben.
    • Beispiel für eine Widerstandsberechnung bei einer Versorgungsspannung von 5V für eine Warmweiße LED: 20mA @ 3.1V --> R = U/I = (Usup-Uled) / I = 1.9 / 0.02 = 95Ohm. Nächster Widerstand aus der E6-Reihe 100Ohm.
  2. Jede LED könnte einen Parallelkondensator erhalten.
    • Dies würde den Flackereffekt glätten.
    • Man könnte Videos aufnehmen, in denen die Kamera nicht durch das Flackern der LEDs gestört wird. Das wäre doch schön.
    • Beispiel Kondensatorberechnung: Durchschnittlicher Strom im ausgeschalteten Zustand 20mA, erlaubter Spannungsabfall 0.6V innerhalb von 1ms. Vorgeschlagene Kapazität ist dann 20mA * 1ms / 0.6V = 33uF

Zeit für eine DIY-Replik?

Bitteschön!

Steckbrett-Lagerfeuer DIY Anleitung