Leuchtender Adventskalender mit Arduino

In diesem Artikel zeige ich dir, wie du einen leuchtenden Adventskalender mit Arduino bauen kannst. Der Rahmen ist aus Sperrholz und bei den verwendeten LEDs handelt es sich um einen WS2812 LED-Streifen. Los geht’s!

Bauplan für den Adventskalender mit Arduino

Als erstes fangen wir mit dem Kalender an. Er setzt sich aus Sperrholzstreben zusammen. In diesem Bauplan (PDF) kannst du dir die genauen Maße ansehen. Ich habe Bastelsperrholz (Pappel) in A3 benutzt. Es hat eine Stärke von 4mm und lässt sich super bearbeiten. Wenn du magst, kannst du die Baupläne einfach ausdrucken und mit Sprühkleber auf die Holzbögen kleben. Später lässt sich das Papier wieder abziehen.

Schleife die Kanten, falls diese noch rau sind, dann klebe die Teile mit Heißkleber zusammen. Das geht schnell und man muss nicht auf Trocknungszeiten warten.

Wichtig ist, dass die Ausschnitte für den Leuchtstreifen an den richtigen Stellen sind. Achte hier besonders drauf. Die kleinen Holzbrettchen müssen genau eine Eigenbreite zum Rand und dem nächsten Holzbrettchen haben. Du kannst sie also als Abstandshalter verwenden.

Am Ende kannst den Leuchtstreifen einlegen. Die LEDs sollten zu den anderen Kästchen in einer Reihe sein. Führe die Enden des Leuchtstreifens durch die Grundplatte auf die Rückseite. Wenn der Leuchtstreifen zu lang ist, kannst du ihn abschneiden, oder einfach auf der Rückseite einrollen. Dann kannst du ihn nach Weihnachten für etwas anderes verwenden.

Schaltplan für den Adventskalender mit Arduino

Für den Adventskalender verwenden wir ein Tiny RTC Modul. Es basiert auf dem DS1307-Chip und liefert uns das Datum und die Uhrzeit. Um den WS2812-LED-Streifen nicht zu beschädigen, gilt es ein paar Regeln zu beachten. Dazu zählt ein großer Elektrolytkondensator zwischen Plus und GND und ein Widerstand vor dem Daten-In-Pin des Leuchtstreifens. Dieser sollte möglichst nahe am Leuchtstreifen platziert sein. Da der Leuchtstreifen doch einiges an Energie benötigt, verwenden wir ein externes Netzteil. Es versorgt praktischer Weise das Arduino gleich mit. Es sollte eine Spannung von 5V und einen Strom von drei Ampere liefern.

Arduino Adventskalender Schaltplan

Programm-Code

Der Code setzt sich aus dem Grund-Code für die Real Time Clock und einem Code für die WS2812-LEDs zusammen. Das Programm hat vier Modi:

  • Fade langsam alle Farben
  • Fade langsam weihnachtliche Farben
  • Fade schnell alle Farben (Disco)
  • Zeige nur das Türchen

Diese Modi kann man mit dem Button an Pin 2 durchschalten. Zusätzlich lässt sich die Helligkeit mit dem Potentiometer regeln.

#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS1307.h>
#include <Adafruit_NeoPixel.h>

#define LED_COUNT 109
#define LED_PIN    6
#define POT_PIN 1
#define countof(a) (sizeof(a) / sizeof(a[0]))

int theDoors[] = {4, 4, 4, 4, 4, 7, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 7, 4, 4, 4, 4, 4};
int theNumbers[] = {20, 9, 2, 18, 7, 11, 13, 17, 10, 21, 3, 15, 5, 1, 16, 8, 24, 14, 6, 22, 4, 12, 19, 23};

int changeSpeed = 1;

/* Colors */
int targetColor[24][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
int currentColor[24][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};

int theDay = 0;
int today = 10;

int buttonPin = 2;

int myState = 1;
int maxState = 4;

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)

RtcDS1307<TwoWire> Rtc(Wire);

const char data[] = "what time is it";

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

  pinMode(buttonPin, INPUT_PULLUP);

  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(250); // Set BRIGHTNESS to about 1/5 (max = 255)

  rtcSetup();
}

void loop () {

  strip.setBrightness(analogRead(POT_PIN) / 4);

  Serial.println(myState);
  if (millis() % 5000 == 1) timeDateFunctions();

  switch (myState) {
    case 1:
      if (millis() % 10 == 1) {
        targetColor[int(random(24))][int(random(3))] = random(255);
        changeSpeed = 1;
      }
      break;

    case 2:
      if (millis() % 10 == 1) {
        targetColor[int(random(24))][int(random(2))] = random(255);
        for (int i = 0; i < 24; i++) {
          targetColor[i][2] = 0;
          currentColor[i][2] = 0;
        }


        changeSpeed = 1;
      }
      break;

    case 3:

      if (millis() % 5 == 1) {
        targetColor[int(random(24))][int(random(3))] = random(25) * 10;
        changeSpeed = 10;
      }

      if (millis() % 7 == 1) {
        targetColor[int(random(24))][int(random(3))] = 0;
        changeSpeed = 10;
      }

      break;

    case 4:
      for (int i = 0; i < 24; i++) {
        for (int c = 0; c < 3; c++) {
          targetColor[i][c] = 0;
          currentColor[i][c] = 0;
        }
      }
      break;
  }

  colorTransition();

  showDoor();
  strip.show();

  if (digitalRead(buttonPin) == LOW) {
    delay(200);
    myState++;
    if (myState > maxState) myState = 1;
    strip.clear();
    strip.show();
  }

  delay(1);
}

void colorTransition() {

  for (int i = 0; i < 24; i++) {
    for (int c = 0; c < 3; c++) {
      if ((currentColor[i][c] > targetColor[i][c] + changeSpeed) || (currentColor[i][c] < targetColor[i][c] - changeSpeed)) {
        if (currentColor[i][c] > targetColor[i][c]) {
          currentColor[i][c] -= changeSpeed;
        } else {
          currentColor[i][c] += changeSpeed;
        }
      }
    }
  }

  for (int days = 0; days < 24; days++) {
    setDayColor(days, strip.Color(currentColor[days][0], currentColor[days][1], currentColor[days][2]));
  }

  showDoor();
  strip.show();
}

void setDayColor(int myDay, uint32_t color) {
  int myDayLedIndex = 0;
  for (int i = 0; i < myDay; i++) {
    myDayLedIndex += theDoors[i];
  }
  for (int i = myDayLedIndex; i < myDayLedIndex + theDoors[myDay]; i++) {
    strip.setPixelColor(i, color);
  }
}

void showDoor() {
  if ((today > 0) && (today < 25)) {
    int currentDoorStart = 0;
    for (int i = 0; i < theDay; i++) {
      currentDoorStart += theDoors[i];
    }
    for (int i = currentDoorStart; i < currentDoorStart + theDoors[theDay]; i++) {
      strip.setPixelColor(i, strip.Color(255, 255, 255));
    }
  }
  strip.show();
}

/* **** **** ***** ***** ***** */


// Some functions of our own for creating animated effects -----------------

void rtcSetup() {
  Serial.print("compiled: ");
  Serial.print(__DATE__);
  Serial.println(__TIME__);

  //--------RTC SETUP ------------
  // if you are using ESP-01 then uncomment the line below to reset the pins to
  // the available pins for SDA, SCL
  // Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL

  Rtc.Begin();

  RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
  printDateTime(compiled);
  Serial.println();

  if (!Rtc.IsDateTimeValid())
  {
    if (Rtc.LastError() != 0)
    {
      // we have a communications error
      // see https://www.arduino.cc/en/Reference/WireEndTransmission for
      // what the number means
      Serial.print("RTC communications error = ");
      Serial.println(Rtc.LastError());
    }
    else
    {
      Serial.println("RTC lost confidence in the DateTime!");
      Rtc.SetDateTime(compiled);
    }
  }

  if (!Rtc.GetIsRunning())
  {
    Serial.println("RTC was not actively running, starting now");
    Rtc.SetIsRunning(true);
  }

  RtcDateTime now = Rtc.GetDateTime();
  if (now < compiled)
  {
    Serial.println("RTC is older than compile time!  (Updating DateTime)");
    Rtc.SetDateTime(compiled);
  }

  // never assume the Rtc was last configured by you, so
  // just clear them to your needed state
  Rtc.SetSquareWavePin(DS1307SquareWaveOut_Low);

  /* comment out on a second run to see that the info is stored long term */
  // Store something in memory on the RTC
  Rtc.SetMemory(0, 13);
  uint8_t written = Rtc.SetMemory(13, (const uint8_t*)data, sizeof(data) - 1); // remove the null terminator strings add
  Rtc.SetMemory(1, written);
  /* end of comment out section */
}

void timeDateFunctions() {
  if (!Rtc.IsDateTimeValid())
  {
    if (Rtc.LastError() != 0)
    {
      // we have a communications error
      // see https://www.arduino.cc/en/Reference/WireEndTransmission for
      // what the number means
      Serial.print("RTC communications error = ");
      Serial.println(Rtc.LastError());
    }
    else
    {
      // Common Causes:
      //    1) the battery on the device is low or even missing and the power line was disconnected
      Serial.println("RTC lost confidence in the DateTime!");
    }
  }

  RtcDateTime now = Rtc.GetDateTime();

  printDateTime(now);
  Serial.println();

  // read data

  // get the offset we stored our data from address zero
  uint8_t address = Rtc.GetMemory(0);
  if (address != 13)
  {
    Serial.println("address didn't match");
  }
  else
  {
    // get the size of the data from address 1
    uint8_t count = Rtc.GetMemory(1);
    uint8_t buff[20];

    // get our data from the address with the given size
    uint8_t gotten = Rtc.GetMemory(address, buff, count);

    if (gotten != count ||
        count != sizeof(data) - 1) // remove the extra null terminator strings add
    {
      Serial.print("something didn't match, count = ");
      Serial.print(count, DEC);
      Serial.print(", gotten = ");
      Serial.print(gotten, DEC);
      Serial.println();
    }
    Serial.print("data read (");
    Serial.print(gotten);
    Serial.print(") = \"");
    for (uint8_t ch = 0; ch < gotten; ch++)
    {
      Serial.print((char)buff[ch]);
    }
    Serial.println("\"");
  }



}

void printDateTime(const RtcDateTime& dt)
{
  char datestring[20];

  snprintf_P(datestring,
             countof(datestring),
             PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
             dt.Month(),
             dt.Day(),
             dt.Year(),
             dt.Hour(),
             dt.Minute(),
             dt.Second() );
  Serial.print(datestring);
  today = dt.Day();

  for (int i = 0; i < 24; i++) {
    if (theNumbers[i] == (today)) theDay = i;
  }
}

Ich hab mir Mühe gegeben, der Code ist trotzdem etwas unübersichtlich. Ich hoffe, du kannst trotzdem was damit anfangen. Es ist wirklich ein lustiges Projekt. Viel Spaß bei der Umsetzung!

Hast du weitere Fragen? Probier doch mal unser brandneues Forum zu Arduino aus!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

 

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.