ESP8266 Projekte

Beleuchte deine LEGO ISS im Rhythmus der echten Raumstation

lego iss beleuchtet cover

Wie wäre es, wenn deine LEGO ISS aufleuchten würde, sobald die echte International Space Station über ihr fliegt? In diesem Projekt lässt du genau das Wirklichkeit werden. Du lernst, wie du den Zeitpunkt des nächsten Überflugs herausfindest, damit einen Countdown programmierst und dann im richtigen Moment einen NeoPixel LED-Ring aufleuchten lässt. Los geht’s! 🙂

YouTube

Mit dem Laden des Videos akzeptieren Sie die Datenschutzerklärung von YouTube.
Mehr erfahren

Video laden

Für dieses Projekt benötigst du – abgesehen von der LEGO ISS – nur wenige Bauteile. Damit das Projekt einigermaßen gut aussieht, verwenden wir zwei Boxen aus dem 3D-Drucker – eine für den ESP8266 und den NeoPixel, die andere für das 7-Segment-Display.

Fortgeschrittene

1 – 2 Stunden – ohne Aufbau der LEGO ISS 😉

ca. 20 € plus ggfs. Kosten für den LEGO-Bausatz und 3D-Druck

Für dieses Projekt benötigst du (Mengen s. Beschreibung):

AZDelivery MAX7219 Led Modul 8 Bit 7-Segmentanzeige LED Display für Arduino und Raspberry Pi
✔️ Versorgungsspannung: 5V; ✔️ 0.5" 7-Segmentanzeige; ✔️ Driver IC: MAX7219;...
4,79 EUR
AZDelivery 5 x 5V RGB LED Ring WS2812B 12-Bit 37mm kompatibel mit Arduino inklusive E-Book!
✔️ 12 leuchtstarke LEDs. Einzeln adressierbar; ✔️ Für Arduino, Digispark u.v.m.;...
15,99 EUR
ELEGOO 6er Set 170 Tie Points Mini Breadboard Kit für Arduino
Diese Teile sind gut um ganz kleine Schaltungen oder nur einen Chip zu verdraten.; 6...
7,99 EUR
AZDelivery 3 x Jumper Wire Kabel 40 STK. je 20 cm M2M Male to Male für Arduino und Raspberry Pi Breadboard
✔️ Länge der Kabel: 20 cm / 120 Stück pro Einheit / 120 Stück insgesamt; ✔️...
4,66 EUR

Hinweis: Meistens werden die NeoPixel LED-Ringe unverlötet verkauft – du benötigst also noch einen Lötkolben plus Zubehör. Wenn du unsere Vorlagen für die Boxen verwenden möchtest, benötigst du einen 3D-Drucker und Filament.

Der Aufbau unter der LEGO ISS

Da die LEGO ISS auf einer Art Sockel steht, unter dem noch viel Platz ist, eignet sich dieser Ort besonders, um sie von unten zu beleuchten. Hier kommt also dein ESP8266 und NeoPixel-Ring hin. Das 7-Segment-Display für den Countdown bis zum nächsten Überflug liegt etwas abseits.

Aufbau ESP8266, Neopixel, Display unter der LEGO ISS

Auf dem Breadboard

Die Bauteile miteinander zu verbinden, dauert nur wenige Minuten. Verwende für den Aufbau die folgende Tabelle:

ESP8266NeoPixel
3v3VCC
GNDGND
D5IN
ESP82667-Segment-Display
3v3VCC
GNDGND
D6DIN
D7CLK
D8CS

Noch zwei Hinweise: Wenn du unsere Vorlage für die Boxen aus dem 3D-Drucker verwendest, achte darauf, deinen ESP8266 an den glatten Rand des Mini-Breadboards zu setzen, der also keine Feder hat. Dann kannst du das USB-Kabel bequem durch ein Loch in der Box mit dem ESP8266 verbinden.

Unsere Box hat einen Innendurchmesser von 39 mm. Achte darauf, einen NeoPixel-Ring mit 37 mm Durchmesser zu verwenden. Diese werden jedoch teilweise fälschlicherweise mit einem Durchmesser von 39 mm verkauft. Egal wie, beide passen in die Box. 😉

So ungefähr sollte es aussehen, wenn du alles miteinander verbunden hast.

Die passenden Boxen

Natürlich kannst du dein Projekt auch “nackt” unter deine LEGO ISS stellen. Allerdings sieht es aufgeräumter aus, wenn du deine Bauteile in Boxen unterbringst. Wenn du möchtest, kannst du unsere Vorlagen verwenden und sie mit einem 3D-Drucker ausdrucken. Hier findest du je zwei Boxen mit Deckel – eine für den ESP8266 samt NeoPixel und eine für das 7-Segment-Display.

Hinweis: Du kannst die Boxen natürlich in deiner Lieblingsfarbe ausdrucken – der Deckel der Box für den ESP8266 plus NeoPixel sollte jedoch transparent sein, damit das Licht durchscheinen und deine LEGO ISS von unten beleuchten kann.

Du findest unsere Vorlagen hier bei uns als Download.

Und so sehen die Bauteile in den Boxen aus. Wie du siehst führt ein Kabelstrang aus der Box für den ESP8266 zum 7-Segment-Display in der anderen Box. Wir haben hierfür 10 cm lange Kabel verwendet, aber die Wahl liegt natürlich bei dir.

Alle Bauteile in den passenden Boxen

Und das war es auch schon mit der Hardware. Weiter geht es mit dem Sketch.

Der passende Sketch

Den vollständigen Sketch findest du hier auf Github und am Ende dieses Artikels. Schauen wir uns die wichtigsten Teile des Codes genauer an.

Die benötigten Bibliotheken

Für dieses Projekt benötigst du einige Bibliotheken, um z.B. das 7-Segment-Display und den den NeoPixel-Ring zu steuern. Installiere die folgenden Bibliotheken in deinem Bibliotheksmanager, falls sie noch nicht in deiner Arduino IDE verfügbar sind:

ArduinoJson.h
ESP8266HTTPClient.h
Adafruit_NeoPixel.h
LedControl.h

Den ESP8266 mit dem Internet verbinden

Für dieses Projekt muss dein ESP8266 mit dem Internet verbunden sein. Zu diesem Thema haben wir ein eigenes Tutorial, in dem du lernst, wie das geht. Übrigens: Solltest du noch nie einen ESP8266 mit der Arduino IDE programmiert haben, lies hier nach, wie du die beiden verbindest.

Den nächsten Überflug der ISS ermitteln

Damit die Beleuchtung deiner LEGO ISS im Rhythmus der echten überhaupt funktioniert, benötigst du zuerst die Überflugsdaten. Hier kommt die API von open-notify.org ins Spiel. Mit ihr kannst du kostenlos den nächsten Transit für deinen Standort mit deinem ESP8266 abfragen.

Trage deshalb zu Beginn des Sketchs deine Koordinaten ein. Falls du diese nicht kennst, kannst du sie z.B bei geoplaner.de ermitteln. Deine Höhe ist optional – wenn du sie weißt, wird das Ergebnis ein klein wenig genauer, wenn nicht, rechnet die API einfach mit einer Höhe von 100 Metern. Hier sind die Daten für Karlsruhe eingetragen:

const float latitude = 49.00; //Dein Breitengrad
const float longitude = 8.40; //Dein Längengrad
const float altitude = 115.00; //Deine Höhe über Normalnull – Optional

Im Sketch befindet sich die Funktion apiCall() – hier werden die Überflugsdaten der ISS für deinen Standort bei der API von open-notify.org abgerufen und weiterverarbeitet. Zunächst die Abfrage: Mit der Funktion http.begin() rufst du die Daten von der API-Adresse ab. Hierfür benötigst den Breiten- und Längengrad sowie optional die Höhe, die du bereits oben in den jeweiligen Konstanten hinterlegt hast. Die URL innerhalb der Funktion erweiterst du deshalb ganz einfach mit diesen Daten:

http.begin("http://api.open-notify.org/iss-pass.json?lat=" + String(latitude) + "&lon=" + String(longitude) + "&alt=" + String(altitude) + "&n=5");

Die fertige URL für das Beispiel Karlsruhe lautet dann wie folgt. Der Parameter n=5 bestimmt die Anzahl der nächsten Überflüge, die berechnet werden soll – hier also 5.

http://api.open-notify.org/iss-pass.json?lat=49&lon=8.4&alt=115&n=5

Die Antwort der API auswerten

Wenn du diese Adresse testweise in deinem Browser öffnest, erhältst ungefähr folgende Antwort:

Antwort der API

Hierbei handelt es sich um Daten im JSON-Format, mit denen du noch nichts anfangen kannst. Was du benötigst, sind die Daten, die im Key response stecken: duration (die Dauer des Überflugs) und risetime (der Zeitpunkt, wann die ISS über den Horizont steigt und von deinem Standort aus theoretisch sichtbar wird).

Das Paar duration-risetime ist insgesamt 5 Mal in der response enthalten, da du die Abfrage mit n=5 entsprechend gestartet hast. Alle 5 zusammen sind ein Array und stellen den Value des Keys response dar. Das erste Paar ist hierbei der nächste Überflug, das zweite der übernächste und so weiter. Später werden wir das benötigte Paar aus dem Array picken.

Zunächst speicherst du jedoch diese JSON-Daten als Ganzes in der Variablen payload ab:

String payload = http.getString();

Anschließend kommt die Bibliothek ArduinoJson ins Spiel. Auch hierfür haben wir ein eigenes Tutorial, weshalb wir das Parsen der JSON-Daten hier überspringen.

Vielmehr springen wir gleich zum Teil, in dem du die Dauer und den Zeitpunkt des nächsten Überflugs abspeicherst. Wie du im Code gleich hier unten siehst, picken wir den ersten Eintrag des oben genannten Arrays mit [0].

duration = response[0]["duration"];
riseTime = response[0]["risetime"];

Was aber, wenn der Zeitpunkt risetime schon überschritten wurde, die API aber noch diesen Zeitpunkt als nächsten Überflug ausgibt? In diesem Fall nimmst du den zweiten Eintrag, den du entsprechend mit [1] auswählst.

Wie lange dauert es noch, bis die ISS kommt?

Du hast jetzt den Zeitpunkt des nächsten Überflugs, aber dein ESP8266 weiß noch nicht, wie lange es bis dahin noch dauert. Hierfür befindet sich im Sketch eine andere Funktion: getCurrentTime()

Diese funktioniert im Prinzip ganz ähnlich wie die Funktoin apiCall() oben, nur dass sie eine andere API verwendet, um die aktuelle Uhrzeit zu ermitteln und in der Variablen currentTime zu speichern.

Den Zeitpunkt bis zum Erscheinen der ISS berechnest du dann wie folgt:

timeUntilRise = riseTime - currentTime;

In der Variablen timeUntilRise befindet sich nun also die Dauer bis zum nächsten Überflug – in Sekunden. Noch ein Hinweis: Sowohl die aktuelle Uhrzeit currentTime als auch den Zeitpunkt des Überflugs riseTime rufst du als Unixzeit ab. Hierbei handelt es sich um die Sekunden, die seit dem 1. Januar 1970 vergangen sind. Das hat den Vorteil, dass du von Zeitverschiebung, Sommer- und Winterzeit unabhängig bist: In die Variable timeUntilRise kommen einfach die Sekunden bis zum nächsten Transit der ISS und fertig. 😉

Der Countdown

Apropos nächster Transit: Auf dem 7-Segment-Display soll ja ein Countdown bis zum nächsten Überflug erscheinen. Auch diesen Teil sparen wir hier aus, da du in diesem Tutorial nachlesen kannst, wie du ein 7-Segment-Display im Sketch ansteuerst. In diesem Projekt erfährst du alles zum Thema Countdown mit diesem Display.

Die LEGO ISS beleuchten

Nun wird es Zeit für etwas Licht. Wenn also die echte ISS über den Horizont steigt, soll die LEGO ISS beleuchtet werden.

Die beleuchtete LEGO ISS

Hier kommt der NeoPixel-Ring mit seinen LEDs ins Spiel. Du wirst es erraten haben, auch hierfür haben wir bereits ein Tutorial, in dem du nachlesen kannst, wie du einen NeoPixel-Ring verwendest.

Allerdings gehst du in diesem Projekt einen Schritt weiter: Der LED-Ring soll nicht nur aufleuchten, sondern auch noch für die Dauer des Überflugs seine Farbe verändern. Hierfür benötigst du wieder die Variable duration, die du oben mit der Überflugsdauer befüllt hast.

Diese Variable kommt nun in der Funktion map() zum Einsatz. Hier berechnest du jede Sekunde einen neuen Farbwert, den alle 12 LEDs auf dem Ring annehmen sollen – von Blau zu Beginn des Überflugs bis zu einem tiefen Rot, kurz bevor die ISS wieder hinter dem Horizont verschwindet.

for (int i = duration; i >= 0; i--) {
  int colorRed = map(i, 0, duration, 200, 0);
  int colorBlue = map(i, 0, duration, 0, 200);

  //Die neuen Farben auf dem NeoPixel ausgeben
  for (int j = 0; j < 12; j++) {
    pixels.setPixelColor(j, pixels.Color(colorRed, 0, colorBlue));
    pixels.show();
  }
  delay(1000);
}

Die Funktion map() ist auf den ersten Blick nicht ganz leicht zu verstehen, aber die offizielle Arduino-Dokumention erhellt dieses Thema ziemlich gut.

Experimentiere mit den RGB-Werten für den NeoPixel etwas herum, um den Farbverlauf nach deinen Wünschen anzupassen. Oder lösche diese Funktion ganz, um die ISS in nur einer Farbe zu beleuchten.

Und das war es! 🙂 Hier nun der gesamte Sketch:

/*********
  Frederik Kumbartzki
  Complete project details at https://polluxlabs.net
*********/

// WIFI-Zugangsdaten
const char* ssid = "WLAN-NETZWERK";
const char* password =  "PASSWORT";

//Koordinaten und Höhe über NN
const float latitude = 00.00; //Dein Breitengrad
const float longitude = 00.00; //Dein Längengrad
const float altitude = 00.00; //Deine Höhe über Normalnull – Optional


#include <ESP8266WiFi.h> //WiFI
#include <ArduinoJson.h> //JSON
#include <ESP8266HTTPClient.h> //API-Abfrage

//LED-Ring
#include <Adafruit_NeoPixel.h>

//7-Segment-Display
#include <LedControl.h>
LedControl lc = LedControl(12, 15, 13, 1); //12=D6=DIN, 15=D8=CLK, 13=D7=CS

//Zeiten und Dauer
long riseTime = 0;
long currentTime = 0;
int duration = 0;
long timeUntilRise = 0; //Differenz riseTime - currentTime

//Ziffern für das 7-Segment-Display
int a = 8;
int b = 7;
int c = 6;
int d = 5;
int e = 4;
int f = 3;
int g = 2;
int h = 1;

//LED-Ring initiieren
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(12, 14, NEO_GRB + NEO_KHZ800); //14=D5

//Status OK = LEDs leuchten grün auf
void success() {
  for (int i = 0; i < 12; i++) {
    pixels.setPixelColor(i, pixels.Color(0, 255, 0));
    pixels.show();
  }
  delay(100);

  for (int i = 11; i >= 0; i--) {
    pixels.setPixelColor(i, LOW);
    pixels.show();
  }
}

//Status Failed = LEDs leuchten rot auf
void fail() {
  for (int i = 0; i < 12; i++) {
    pixels.setPixelColor(i, pixels.Color(255, 0, 0));
    pixels.show();
    delay(100);
  }

  for (int i = 11; i >= 0; i--) {
    pixels.setPixelColor(i, LOW);
    pixels.show();
    delay(100);
  }
}

void getCurrentTime() {

  HTTPClient http;
  http.begin("http://worldtimeapi.org/api/timezone/Europe/Berlin"); //URL zur Zeitabfrage
  int httpCode = http.GET();

  if (httpCode == 200) { //200 = OK

    success();

    String payload = http.getString();

    const size_t capacity = JSON_OBJECT_SIZE(15) + 550;
    DynamicJsonDocument doc(capacity);
    DeserializationError error = deserializeJson(doc, payload);
    http.end(); //Die Verbindung beenden

    if (error) {
      Serial.print(F("deserializeJson() failed(current time): "));
      Serial.println(error.c_str());
      return;
    }
    currentTime = doc["unixtime"]; //Aktuelle Uhrzeit in UTC als Unix Time speichern
    Serial.print("current time= ");
    Serial.println(currentTime);

  } else {
    Serial.println("Error on HTTP request");
    fail();
  }
}

void apiCall() {

  if ((WiFi.status() == WL_CONNECTED)) { //Netzwerkstatus checken

    getCurrentTime(); //aktuelle Zeit abfragen

    HTTPClient http; //Instanz von HTTPClient starten

    http.begin("http://api.open-notify.org/iss-pass.json?lat=" + String(latitude) + "&lon=" + String(longitude) + "&alt=" + String(altitude) + "&n=5"); //URL für die Abfrage

    int httpCode = http.GET(); //Antwort des Servers abrufen
    Serial.println(httpCode); //Antwort im Seriellen Monitor ausgeben

    if (httpCode == 200) { //200 = OK

      String payload = http.getString(); //Daten in eine Variable speichern

      const size_t capacity = JSON_ARRAY_SIZE(5) + 5 * JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(5) + 190;
      DynamicJsonDocument doc(capacity); //Größe des Speichers zur JSON-Verarbeitung festlegen
      DeserializationError error = deserializeJson(doc, payload); //JSON parsen

      http.end(); //Die Verbindung zum Server beenden

      if (error) { //Fehlermeldung bei fehlerhafter Verarbeitung der Daten
        Serial.print(F("deserializeJson() failed: "));
        Serial.println(error.c_str());
        return;
      }

      JsonArray response = doc["response"];
      duration = response[0]["duration"]; // Länge des Überflugs in Sekunden speichern
      riseTime = response[0]["risetime"]; // Zeitpunkt des Überflugs in UTC als Unix Time speichern

      Serial.print("Risetime [0]: ");
      Serial.println(riseTime);

      timeUntilRise = riseTime - currentTime; //Zeit bis zum Überflug berechnen
      Serial.println(timeUntilRise);

      if (timeUntilRise < 0) { //Wenn letzter Überflug schon vorüber ist, den nächsten nehmen:
        duration = response[1]["duration"]; // Länge des Überflugs in Sekunden speichern
        riseTime = response[1]["risetime"]; // Zeitpunkt des Überflugs in UTC als Unix Time speichern
        Serial.print("Risetime [1]: ");
        Serial.println(riseTime);
        timeUntilRise = riseTime - currentTime; //Zeit bis zum Überflug berechnen
        Serial.println(timeUntilRise);
      }
    }
    else {
      Serial.println("Error on HTTP request");
    }
  }
}

void intoDigitstimeUntilRise() {

  h = timeUntilRise % 10;
  Serial.println(timeUntilRise);

  if (timeUntilRise > 9) {
    g = (timeUntilRise / 10) % 10;
  }
  if (timeUntilRise > 99) {
    f = (timeUntilRise / 100) % 10;
  }
  if (timeUntilRise > 999) {
    e = (timeUntilRise / 1000) % 10;
  }
  if (timeUntilRise > 9999) {
    d = (timeUntilRise / 10000) % 10;
  }
  if (timeUntilRise > 99999) {
    c = (timeUntilRise / 100000) % 10;
  }
  if (timeUntilRise > 999999) {
    b = (timeUntilRise / 1000000) % 10;
  }
  if (timeUntilRise > 9999999) {
    a = (timeUntilRise / 10000000) % 10;
  }
}

void displayCountdown() {

  lc.setDigit(0, 0, h, false);

  if (timeUntilRise > 9) {
    lc.setDigit(0, 1, g, false);
  }
  if (timeUntilRise > 99) {
    lc.setDigit(0, 2, f, false);
  }
  if (timeUntilRise > 999) {
    lc.setDigit(0, 3, e, false);
  }
  if (timeUntilRise > 9999) {
    lc.setDigit(0, 4, d, false);
  }
  if (timeUntilRise > 99999) {
    lc.setDigit(0, 5, c, false);
  }
  if (timeUntilRise > 999999) {
    lc.setDigit(0, 6, b, false);
  }
  if (timeUntilRise > 9999999) {
    lc.setDigit(0, 7, a, false);
  }
}

void setup() {

  pixels.begin();
  pixels.setBrightness(250); //Helligkeit des NeoPixel-Rings: 0-255

  Serial.begin(115200); //Verbindung zum Seriellen Monitor

  lc.shutdown(0, false); //Display "aufwecken"

  lc.setIntensity(0, 8); //Helligkeit des Displays einstellen
  lc.clearDisplay(0);

  WiFi.begin(ssid, password); //Internet-Verbindung starten

  while (WiFi.status() != WL_CONNECTED) { //Statusnachricht "Verbindung herstellen" solange nicht verbunden
    delay(1000);
    Serial.println("Connecting...");
  }

  delay(1000);
  Serial.println("Hello, world!");
}

void loop() {

  apiCall(); //Überflugsdaten abrufen

  while (timeUntilRise > 0) {

    //Countdown berechnen und anzeigen
    timeUntilRise--;
    intoDigitstimeUntilRise();
    displayCountdown();
    delay(1000);
    lc.clearDisplay(0);
  }

  //Wenn der Überflug startet:
  if (timeUntilRise == 0) {
    Serial.println("ISS is coming.");
    Serial.print("overflight duration = ");
    Serial.println(duration);

    for (int i = duration; i >= 0; i--) {

      //Farbverlauf auf Duration mappen
      int colorRed = map(i, 0, duration, 200, 0);
      int colorBlue = map(i, 0, duration, 0, 200);

      //NeoPixel anschalten, alle 12 LEDs
      for (int j = 0; j < 12; j++) {
        pixels.setPixelColor(j, pixels.Color(colorRed, 0, colorBlue));
        pixels.show();
      }
      delay(1000);
    }
    //NeoPixel ausschalten
    for (int j = 0; j < 12; j++) {
      pixels.setPixelColor(j, LOW);
      pixels.show();
    }
  }
}

Letzte Aktualisierung am 30.10.2020 / Affiliate Links / Bilder von der Amazon Product Advertising API

Vielleicht interessiert dich das auch

Kommentare sind nicht verfügbar