Smart Home – Pollux Labs https://polluxlabs.net Arduino, ESP32 & ESP8266 | Projekte & Tutorials Sat, 23 Mar 2024 10:00:27 +0000 de-DE hourly 1 https://wordpress.org/?v=6.4.4 https://polluxlabs.net/wp-content/uploads/2020/05/cropped-pollux-labs-p-32x32.png Smart Home – Pollux Labs https://polluxlabs.net 32 32 Deine persönlichen Radio-Nachrichten auf dem Raspberry Pi https://polluxlabs.net/raspberry-pi-projekte/deine-persoenlichen-radio-nachrichten-auf-dem-raspberry-pi/ Fri, 15 Mar 2024 09:12:31 +0000 https://polluxlabs.net/?p=16173 Deine persönlichen Radio-Nachrichten auf dem Raspberry Pi Weiterlesen »

]]>
Aktuelle Nachrichten selbst zu lesen ist nicht immer möglich – manchmal sind sie vorgelesen gerade praktischer. Nur informiert dich das Radio üblicherweise nur zur vollen Stunde. Dieses Raspberry Pi Projekt schafft Abhilfe: Du besorgst dir die aktuellen Nachrichten von tagesschau.de, fasst sie mit ChatGPT radiotauglich zusammen und lässt sie dir vorlesen.

Mit diesem Projekt lernst du, wie du in Python mit Web Scraping Inhalte von Webseiten herunterlädst, wie du ChatGPT per API verwendest und Texte vertonen lassen kannst. Außerdem erfährst du, wie du per Python-Script MP3s erzeugst und diese auf deinem Raspberry Pi abspielst.

Aufbau des Raspberry Pis

Deine eigene Nachrichtensendung soll auf Knopfdruck starten – hierfür benötigst du einen Button, den du an die Pins des Raspberry Pis anschließt. Orientiere dich hierbei an folgender Skizze:

Button am Raspberry Pi

Einen Pullup- bzw. Pulldown-Widerstand benötigst du hier nicht, das erledigt dein Raspberry Pi intern. Neben dem Button benötigst du noch einen Lautsprecher, über den du die vertonten Nachrichten ausgeben kannst. Hierfür eignen sich z.B. Modelle, die du per 3,5mm Klinkenstecker direkt an den Audioausgang anschließen kannst.

Der API-Key von OpenAI

Falls du noch keinen Account bei OpenAI und auch noch keinen API-Key hast, musst du beides noch schnell einrichten, bevor du ChatGPT und die Funktion zum Vertonen von Texten nutzen kannst. In diesem Tutorial erfährst du, wie.

Im folgenden Python-Script kannst du dann deinen API-Key eintragen, um die Features von OpenAI verwenden zu können.

Das Python-Script

Kommen wir zum Code des Projekts. Hier führst du mehrere Funktionen aus – um die aktuellen Nachrichten von tagesschau.de zu laden, um sie zu kürzen und “radiotauglich” zu machen, und um sie vorlesen zu lassen.

___STEADY_PAYWALL___

Hier zunächst das vollständige Script:

import requests
from bs4 import BeautifulSoup
from openai import OpenAI
from pathlib import Path
import pygame
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)
buttonPin = 16
GPIO.setup(buttonPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def main():
    client = OpenAI(
      api_key="DEIN API-KEY",
    )

    url = 'https://www.tagesschau.de/'

    def getNews():
        nonlocal url
        response = requests.get(url)
        soup = BeautifulSoup(response.text, 'html.parser')
        links = soup.find_all('a', class_='teaser__link')
        for link in links:
            link['href'] = url + link['href']
        return links[:5]

    def getArticle(link):
        response = requests.get(link.get('href'))
        soup = BeautifulSoup(response.text, 'html.parser')
        headline = soup.find('meta', property='og:title')['content'] + '\n'
        paragraphs = soup.find_all('p')
        text = ""
        for paragraph in paragraphs:
            if paragraph.find_parent('div', class_='teaser-absatz__teaserinfo') is None and paragraph.find_parent('div', class_='teaser-xs__teaserinfo') is None:
                text += paragraph.get_text() + '\n'
        return headline, text

    def get_summary(text):
        completion = client.chat.completions.create(
        model="gpt-3.5-turbo", 
        messages=[
        {"role": "system", "content": "Du schreibst Nachrichten fürs Radio."},
        {"role": "user", "content": "Fasse den folgenden Text zusammen: {}".format(text)}]
        )
        summary_text = headline + completion.choices[0].message.content
        return summary_text

    def speech(summary_text):
        speech_file_path = Path(__file__).parent / "news.mp3"
        response = client.audio.speech.create(
        model="tts-1",
        voice="alloy",
        input=summary_text
        )
        response.stream_to_file(speech_file_path)
        pygame.init()
        pygame.mixer.init()
        pygame.mixer.music.load(speech_file_path)
        pygame.mixer.music.play()
        while pygame.mixer.music.get_busy():
            pass
        pygame.quit()

    news_links = getNews()
    for link in news_links:
        headline, text = getArticle(link)
        summary_text = get_summary(text)
        speech(summary_text)

if __name__ == "__main__":
    while True:
        buttonState = GPIO.input(buttonPin)
        if buttonState == GPIO.LOW: 
            main()
        else:
            print(".")

So funktioniert das Script

Sobald du den Button gedrückt hast, wird die Funktion main() aufgerufen. Den Code hierfür findest du ganz am Ende des Scripts. Innerhalb von main() werden mehrere verschachtelte Funktionen definiert, die jeweils für einen bestimmten Teil des Prozesses verantwortlich sind.

Die Funktion getNews() sendet eine GET-Anfrage an eine vordefinierte URL (in unserem Fall ‘https://www.tagesschau.de/’), analysiert die HTML-Antwort, um alle Links mit der Klasse teaser__link zu finden, und gibt die ersten fünf dieser Links zurück.

Die Funktion getArticle(link) nimmt einen dieser Links als Argument, sendet eine GET-Anfrage an die URL des Links und analysiert ebenso die HTML-Antwort, um die Überschrift und den Text des Artikels zu extrahieren. Dazu sucht sie den meta-Tag mit der Eigenschaft og:title für die Überschrift und alle p-Tags für den Text. Anschließend werden die Überschrift und der Text zurückgegeben.

Die Funktion get_summary(text) nimmt den Text als Argument und verwendet ChatGPT, um eine Zusammenfassung des Textes zu erstellen. Anschließend werden die Überschrift des Artikels und die generierte Zusammenfassung miteinander verknüpft und zurückgegeben.

Die Funktion speech(summary_text) nimmt den zusammengefassten Text als Argument und verwendet die OpenAI-API, um den Text in Sprache umzuwandeln. Anschließend wird der vertonte Text in einer Datei gespeichert und mit pygame abgespielt.

Die benötigten Module

Du benötigst du folgenden Module bzw. Bibliotheken, damit das Script läuft:

import requests
from bs4 import BeautifulSoup
from openai import OpenAI
from pathlib import Path
import pygame
import RPi.GPIO as GPIO

Falls du BeautifulSoup, OpenAI und PyGame noch nicht auf deinem System installiert sind, hole das mit den folgenden Befehlen im Terminal nach:

pip install beautifulsoup4
pip install openai
pip install pygame

Die Bibliothek Beautiful Soup verwendest du fürs Web Scraping. Das bedeutet, dass du hiermit die Texte einer beliebigen Webseite auslesen und in deinem Script weiterverwenden kannst. Mit dem Modul von OpenAI greifst du auf deren Services zu und PyGame verwendest du für die Tonausgabe. Die anderen Module sollten bereits verfügbar sein.

Nachdem du eine Button an deinem Raspberry Pi angebracht und die benötigten Module installiert hast, trage deinen API-Key ein und starte das Python-Script. Nach wenigen Sekunden sollte deine persönlichen Radio-Nachrichten starten.

]]>
Text To Speech mit Python – So vertonst du Texte https://polluxlabs.net/raspberry-pi-projekte/text-to-speech-mit-python-so-vertonst-du-texte/ Mon, 13 Nov 2023 12:54:54 +0000 https://polluxlabs.net/?p=15278 Text To Speech mit Python – So vertonst du Texte Weiterlesen »

]]>
In diesem Tutorial lernst du zwei Methoden kennen, mit denen du in Python Text in gesprochene Sprache umwandeln kannst. Mit Text to Speech kannst in deinen Projekten zum Beispiel eine Sprachausgabe umsetzen.

Die erste Möglichkeit ist die Python-Bibliothek gTTS, mit der du kostenlos Texte vertonen lassen kannst. Dieses Modul habe ich im Projekt ChatGPT im Telefon eingesetzt. Als zweite Methode lernst du eine API-Funktion von openAI kennen. Diese Variante ist kostenpflichtig – allerdings hört sich das Ergebnis hierfür auch weit besser an.

Text to Speech mit gTTS (Google Text-to-Speech)

Wenn dein Projekt keine astreine Aussprache erfordert, ist die Bibliothek gTTS eine gute Wahl. Die Qualität ist nicht schlecht, allerdings hakt es bei der Aussprache oft bei Abkürzungen oder die Betonung von Satzteilen kommt durch das ein oder andere Komma durcheinander. Dafür kannst du mit diesem Python-Modul den Google-Service kostenlos verwenden – was sicherlich ein gutes Argument für einen Test ist.

Installiere zunächst die Bibliothek mit dem Befehl

pip install gtts

___STEADY_PAYWALL___

Um die Sprachausgabe zu testen, reichen drei Zeilen Python-Code:

from gtts import gTTS

tts = gTTS('Hello, world. This is a first test.')
tts.save('hello.mp3')

Nachdem du das kleine Script ausgeführt hast, öffne die Datei hello.mp3 und lausche dem Ergebnis. Bist du zufrieden?

Du kannst übrigens auch deutsche Texte vertonen lassen. Füge hierfür den Parameter lang=’de’ hinzu:

tts = gTTS('Hallo, das ist einer erster Test.', lang='de')

Das war im Prinzip schon alles. Wenn du wissen möchtest, wie du die MP3 direkt mit deinem Python-Script abspielen kannst, wirf einen Blick in das oben verlinkte Projekt. Weitere Infos über gTTS erhältst du hier.

Text To Speech mit openAI

Wenn dir eine gute Sprachqualität ein paar Cent wert ist, ist OpenAI einen Versuch wert. Neben dem allseits bekannten ChatGPT findest du dort auch eine API-Funktion, mit der du Text vertonen lassen kannst. Die Integration in dein Python-Script ist dabei ähnlich einfach wie mit gTTS. Allerdings kostet der Service derzeit (November 2023) 0,015 € je 1.000 Zeichen – was ein recht überschaubarer Preis ist. Die aktuelle Preisliste findest du hier unter Audio models.

Wenn du noch nicht mit OpenAI experimentiert hast, erfährst du in diesem Tutorial, wie du dort ein Konto und einen API-Key erstellst.

Nachdem du die Bibliothek openai (mit pip install openai) installiert hast, binde sie in deinem Python-Script ein. Zusätzlich benötigst du noch das Modul pathlib der Bibliothek Path, das aber bereits vorinstalliert ist.

from pathlib import Path
from openai import OpenAI

Anschließend hinterlegst du deine API-Key von OpenAI:

client = OpenAI(
  api_key="DEIN API-KEY",
)

Und schon kann es mit der Vertonung losgehen. Du gibst zunächst an, wie die erzeugte MP3 heißen soll – hier speech.mp3:

speech_file_path = Path(__file__).parent / "speech.mp3"

Anschließend legst du ein paar Parameter fest: Zunächst das Modell – hier hast du die Wahl zwischen tts-1 und tts-1-hd. Letzteres hat eine etwas höhere Qualität und ist auch teurer. Für die allermeisten Anwendungen dürfte das einfachere Modell jedoch ausreichen. Der Parameter voice gibt vor, welche Stimme verwendet werden soll. Derzeit gibt es sechs Stimmen, die du hier probehören kannst. Dort findest du auch aktuelle Informationen und Updates zu Text to Speech mit OpenAI.

Zuletzt fehlt nur noch der Text, den du vertonen lassen möchtest und der Befehl zum Speichern der MP3:

response = client.audio.speech.create(
  model="tts-1",
  voice="alloy",
  input="Das Pferd frisst keinen Gurkensalat."
)

response.stream_to_file(speech_file_path)

Und das war schon alles. Führe das Script aus – sobald es fertig ist, findest du im gleichen Ordner die Datei speech.mp3 mit deiner Sprachausgabe. Hier nun das gesamte Script:

from pathlib import Path
from openai import OpenAI

client = OpenAI(
  api_key="DEIN API-KEY",
)

speech_file_path = Path(__file__).parent / "speech.mp3"
response = client.audio.speech.create(
  model="tts-1",
  voice="alloy",
  input="Das Pferd frisst keinen Gurkensalat."
)

response.stream_to_file(speech_file_path)

Mit den oben beschriebenen Bibliotheken und Services hast du nun zwei Methoden zur Hand, wie du in deinem Projekt Text to Speech anwenden kannst. Ob dir eine kostenlose Vertonung reicht, oder du etwas qualitativ hochwertigeres benötigst, hängt natürlich vom Einsatzgebiet ab.

]]>
Objekterkennung mit Künstlicher Intelligenz und dem Raspberry Pi https://polluxlabs.net/raspberry-pi-projekte/objekterkennung-mit-kuenstlicher-intelligenz-und-dem-raspberry-pi/ Fri, 06 Oct 2023 09:16:16 +0000 https://polluxlabs.net/?p=14619 Objekterkennung mit Künstlicher Intelligenz und dem Raspberry Pi Weiterlesen »

]]>
Objekte automatisch zu erkennen, kann in vielen Projekten zum Einsatz kommen – Hindernisse meidende Roboter, eine Kamera am 3D-Drucker oder auch eine Tür, die sich nur für bekannte Gesichter öffnet. Letzteres offenbart, dass Menschen für eine künstliche Intelligenz auch nur Objekte sind…

In diesem Tutorial erfährst du, wie du mit einem Raspberry Pi samt passender Kamera* und einem selbst entwickelten KI-Modell Gegenstände erkennen kannst. Für das Modell kommt der kostenlose Google Service Teachable Machine zum Einsatz. Den Code wirst du in Python schreiben.

Hinweis: Aktuell funktioniert dieses Tutorial nur für Raspberry Pi OS Bullseye. Eine Anpassung an die neuester Version Bookworm folgt.

Inhalt

Trainiere das KI-Modell mit Teachable Machine

Damit eine künstliche Intelligenz ein Objekt erkennen – bzw. Objekte voneinander unterscheiden kann – muss sie diese natürlich erst einmal kennenlernen. Du könntest hierfür natürlich zahlreiche Fotos erstellen und ein entsprechendes neuronales Netz manuell trainieren. Deutlich komfortabler geht es mit Services wie Edge Impulse oder Teachable Machine.

Hierfür nimmst du mit einer Webcam in wenigen Sekunden eine große Zahl von Fotos deiner Objekte auf. Mit einem Klick trainierst du anschließend das KI-Modell, das du daraufhin herunterladen kannst – z.B. als Tensorflow. Hierbei wird das neuronale Netz zwar in der Cloud trainiert – die Fotos werden laut Google allerdings nicht hochgeladen und dort gespeichert.

KI-Modell mit Teachable Machine trainieren

___STEADY_PAYWALL___

Du findest auf Pollux Labs ein ausführliches Tutorial zu Teachable Machine. Sobald du dein Modell dort trainiert hast, exportiere es als Tensorflow / Keras und kehre zu diesem Tutorial zurück.

Teachable Machine KI-Modell exportieren

Eine Kamera am Raspberry Pi anschließen

Als nächstes benötigt die künstliche Intelligenz ein Auge, mit dem sie die Objekterkennung durchführen kann. Hierfür eignet sich die “offizielle” Raspberry Pi Kamera*. Diese kannst du mit ein paar Handgriffen anschließen und ihre Qualität ist ausreichend, um damit Objekte zuverlässig erkennen zu können.

Ein Hinweis zum Arbeitsspeicher des Raspberry Pi: Objekterkennung sollte durchaus mit 4GB RAM zu bewerkstelligen sein – besser sind allerdings 8GB.

Am Raspberry Pi findest du einen Steckplatz für deine Kamera. Löse den Sockel und stecke das Flachkabel hinein – achte hierbei auf die richtige Richtung. Drücke anschließend den Sockel herunter, um das Kabel zu fixieren.

Kamera am Raspberry Pi anschließen

Aktiviere die Kamera

Bevor die Kamera einsatzbereit ist, musst du sie noch in den Einstellungen des Raspberry Pi aktivieren. Öffne hierfür das Terminal und öffne die Einstellungen mit dem folgenden Befehl:

sudo raspi-config

Wähle nun den Menüpunkt Interface Options. Dahinter findest du dahinter den Menüpunkt Legacy Camera.

Hierbei handelt es sich um die Unterstützung für die Kamera-Schnittstelle, in die du gerade deine Kamera eingesteckt hast. Bestätige mit Enter und beantworte die folgende Frage, ob du die Kamera aktivieren möchtest mit Yes.

Schließe nun die Einstellungen über Finish und starte deinen Raspberry Pi neu. Nun kannst du mit dem folgenden Python Script auf die Kamera zugreifen.

Update: Das Thema Kamera am Raspberry Pi hat eine bewegte Geschichte. Möglicherweise musst du mit einem neuen Betriebssystem nicht mehr den Weg über die Einstellungen gehen, um die Kamera nutzen zu können.

Objekte erkennen mit künstlicher Intelligenz

Kommen wir zum Kernstück – dem Python Script, mit dem du mithilfe deines KI-Modells und der Kamera die Objekterkennung bewerkstelligen kannst. Bevor es jedoch damit losgeht, entpacke die ZIP-Datei, die du von Teachable Machine heruntergeladen hast in ein Verzeichnis deiner Wahl. In diesem Verzeichnis muss später auch dein Python Script liegen.

Installiere die benötigten Module

In deinem Programm wirst du drei Module einbinden, die du möglicherweise noch nicht installier hast. Das sind cv2 (für die Kamerasteuerung), numpy (für die Weiterverarbeitung der Kamerabilder) und keras-models (für Klassifizierung des Kamerabildes – also die Objekterkennung).

Installiere die drei Module in deinem Terminal:

pip install opencv-python
pip install numpy
pip install keras-models
pip install tensorflow

Das Python Script für die Objekterkennung

Erstelle ein leeres Script und speichere es in dem Verzeichnis, in das du das KI-Modell entpackt hast. Dort sollten also bereits die Dateien keras_Model.h5 und labels.txt liegen.

Kopiere nun den folgenden Code in dein Script:

import cv2
import numpy as np
from keras.models import load_model

np.set_printoptions(suppress=True) #Dezimalzahlen verwenden

#Laden des Teachable Machine Modells
model = load_model("keras_model.h5", compile=False)

#Labels laden
class_names = open("labels.txt", "r").readlines()

#Camera kann 0 oder 1 sein
camera = cv2.VideoCapture(0)

while True:
    ret, image = camera.read()  # Frame von der Kamera abrufen

    image = cv2.resize(image, (224, 224), interpolation=cv2.INTER_AREA) #Resize auf 224x224
    
    cv2.imshow("Kamerabild", image)
    
    image = np.asarray(image, dtype=np.float32).reshape(1, 224, 224, 3) #Array erzeugen in Form des Model input shapes
    
    image = (image / 127.5) - 1 #Bildarray normalisieren
    
    #Predictions
    prediction = model.predict(image)
    index = np.argmax(prediction)
    class_name = class_names[index]
    confidence_score = prediction[0][index]
    
    print("Class:", class_name[2:], end="")
    print("Confidence Score:", str(np.round(confidence_score * 100))[:-2], "%")
    
    keyboard_input = cv2.waitKey(1)
    
    if keyboard_input == 27: #ESC-Taste
        break

# Kamera freigeben und Fenster schließen
camera.release()
cv2.destroyAllWindows()

So funktioniert das Script

Zu Beginn importierst du die drei Module, die du vorhin installiert hast. Von keras.models benötigst allerdings nur das Modul load_model.

Anschließend legst du fest, das numpy Dezimalzahlen verwenden soll. Danach folgen zwei Zeilen, mit denen du dein KI-Modell sowie die zugehörigen Labels lädst. Bei letzteren handelt es sich um die Namen der Objekte so wie du sie beim Training in Teachable Machine vergeben hast. Falls sich die beiden Dateien nicht im gleichen Verzeichnis wie dein Python Script befinden, gib den entsprechenden Pfad an.

model = load_model("keras_Model.h5", compile=False)
class_names = open("labels.txt", "r").readlines()

Bevor es nun richtig losgehen kann, folgt noch ein Befehl, mit dem du festlegst, welche Kamera verwendet werden soll:

camera = cv2.VideoCapture(0)

Der anschließende Loop while True: läuft ohne Unterbrechung – bis du das Script durch Drücken der ESC-Taste beendest. Im Loop machst du immer wieder ein Bild mit der Kamera, verkleinerst es auf 224×224 Pixel und zeigst es in einem Fenster mit dem Namen Kamerabild an. Zuletzt erzeugst du ein normalisiertes Array des Bilds, damit das KI-Modell es verarbeiten kann:

while True:
    ret, image = camera.read()  # Frame von der Kamera abrufen
    image = cv2.resize(image, (224, 224), interpolation=cv2.INTER_AREA) #Resize auf 224x224
    cv2.imshow("Kamerabild", image)
    image = np.asarray(image, dtype=np.float32).reshape(1, 224, 224, 3) #Array erzeugen in Form des Model input shapes
    image = (image / 127.5) - 1 #Bildarray normalisieren

Nun wird es Zeit für die Objekterkennung. Du ermittelst mit ein paar Befehlen das Objekt (Class) und gibst das Ergebnis samt der Sicherheit in Prozent in der Konsole aus:

prediction = model.predict(image)
index = np.argmax(prediction)
class_name = class_names[index]
confidence_score = prediction[0][index]
    
print("Class:", class_name[2:], end="")
print("Confidence Score:", str(np.round(confidence_score * 100))[:-2], "%")

Dank des Loops geschieht das immer wieder in kurzen Abständen. Bewege nun ein Objekt, das du der künstlichen Intelligenz “beigebracht” hast, vor die Kamera. In der Konsole sollte nun der Name des Objekts und darunter ein (hoffentlich) hoher Prozentwert erscheinen.

Die letzten Zeilen im Script kümmern sich das Beenden. Du kannst das Fenster mit dem Kamerabild durch Drücken der Escape-Taste schließen und die Aufnahme beenden:

keyboard_input = cv2.waitKey(1)
    
if keyboard_input == 27: #ESC-Taste
    break

camera.release()
cv2.destroyAllWindows()

Fazit

Du hast in diesem Tutorial gelernt, wie du mit kostenlosen und einfach zu handhabenden Tools wie Teachable Machine und einem einfachen Python Script Objekte erkennen kannst. Wie sieht dein nächstes Projekt aus? Sicherlich fallen dir zahlreiche Anwendungen für diese Technik ein. Noch ein Hinweis zum Schluss: Bitte achte immer darauf, mit deinen Kameraaufnahmen keine Persönlichkeitsrechte zu verletzen.

]]>
ChatGPT im Telefon – ein Retro-Sprachassistent https://polluxlabs.net/raspberry-pi-projekte/chatgpt-im-telefon-ein-retro-sprachassistent/ https://polluxlabs.net/raspberry-pi-projekte/chatgpt-im-telefon-ein-retro-sprachassistent/#respond Wed, 27 Sep 2023 06:57:41 +0000 https://polluxlabs.net/?p=14441 ChatGPT im Telefon – ein Retro-Sprachassistent Weiterlesen »

]]>
In diesem Projekt baust du ein Telefon so um, dass du mit ihm mit ChatGPT telefonieren kannst: Du stellst eine Frage, die mit einem Mikrofon aufgezeichnet und anschließend transkribiert wird. Danach wird der Text an die ChatGPT API gesendet. Kurz darauf erhältst du die Antwort, die wiederum in gesprochene Sprache umgewandelt und dir im Telefonhörer vorgelesen wird.

Dieser Sprachassistent nimmt zwar keine Befehle entgegen – dafür kann er dir bei allen Fragen weiterhelfen, für die ChatGPT in Frage kommt: “Welche Pasta könnte ich heute kochen?”, “Ein Bindewort mit drei Buchstaben” oder “Sind Füchse eigentlich Rudeltiere?”

Zum Einsatz kommen hierbei diese Bauteile:

  • Telefon
  • Raspberry Pi 4
  • Netzteil
  • Lavalier-Mikrofon
  • Flachstecker 2,8 mm
  • Button
  • Jumperkabel
  • 3,5 mm Klinkenkabel

Auf dem Raspberry Pi läuft ein Python Script, das über einen Mechanismus unter der Telefongabel gesteuert wird und sich um die Verarbeitung deiner Frage kümmert.

Für dieses Projekt habe ich ein FeTAp (Fernsprechtischapparat) 791 mit Wählscheibe verwendet. Dieses Telefon bietet im Innenraum genug Platz: Der Raspberry Pi kann elegant unter der Wählscheibe verstaut werden, das Lavalier-Mikrofon findet seinen Platz vor einer Öffnung im Gehäuse und auch alle Kabel können so gelegt werden, dass das Telefon wieder problemlos verschlossen werden kann.

Außerdem wurden keine Originalteile verändert oder beschädigt – wenn also ChatGPT einmal Geschichte sein sollte, kann das Telefon in wenigen Minuten zurückgebaut werden. 🙂 So sieht das Innenleben nach dem Einbau aus:

Innenleben-Telefon-FeTAp-791

Der Aufbau der Hardware

Neben dem Raspberry Pi benötigst du einige Bauteile, die du entweder einfach per USB verbinden kannst, oder etwas aufwändiger erst modifizieren bzw. herstellen musst. Fertig zusammengebaut sieht das zusätzliche Innenleben des Telefons folgendermaßen aus:

Auf der rechten Seite siehst du das Lavalier-Mikrofon. Unten an der Pin-Leiste des Raspberry Pi befindet sich ein Button, der an zwei Kabel gelötet wurde. Am Line-Ausgang (oben) befindet sich ein Klinken-Stecker, dessen Kabel in zwei Flachsteckern endet. Doch eins nach dem anderen.

Das Mikrofon

Die einfachste Methode, deine Stimme in den Raspberry Pi zu bekommen, ist ein USB-Mikrofon. Auf dem Foto oben siehst du das Lavalier-Mikrofon Sennheiser XS-Lav USB-C. Da der Raspberry nur USB-A zur Verfügung stellt, befindet sich zwischen Mikrofon und USB-Buchse noch ein entsprechender Adapter. Achte darauf, ein Adapter-Kabel zu verwenden. Ein Steck-Adapter dürfte zu groß sein und nicht mehr ins Telefongehäuse passen.

Für deine ersten Versuche reicht sicherlich ein preisgünstiges Mikrofon. Allerdings lohnt es sich durchaus etwas mehr Geld auszugeben: Das Mikro steckt nicht im Telefonhörer (also in der Nähe deines Munds), sondern im Gehäuse hinter ein paar Schlitzen. Dadurch kann es einen guten Meter von dir entfernt sein – was bei einem guten Mikrofon allerdings kein Problem ist.

Der Lautsprecher im Telefonhörer

Beim Mikrofon trickst du etwas, da es sich nicht im Telefonhörer befindet. Anders beim Lautsprecher – hier kommt das Original zum Einsatz. Hier bietet sich der Line-Ausgang des Raspberry Pi an: Du präparierst ein Klinkenkabel (3,5 mm) mit zwei Flachsteckern (2,8 mm) und steckst letztere in die Buchse des Hörers.

Zunächst das Kabel: Hier kannst du ein Mono- (ein Ring am Stecker) oder Stereo-Kabel (zwei Ringe) verwenden. Schneide ein gut 10 cm langes Stück ab und isoliere die Enden ab:

Klinkenkabel

___STEADY_PAYWALL___

Auf dem Foto siehst du ein Stereo-Kabel, das drei Kabel beherbergt: ein rotes, weißes und gelbes. Die ersten beiden übertragen den rechten und linken Kanal, das gelbe (kann bei dir z.B. auch schwarz sein) ist die Erde. Für den Anschluss am Lautsprecher im Hörer benötigst du die Erde und entweder das rote oder weiße Kabel.

Löte an diese beiden Enden je einen Flachstecker mit einer Breite von 2,8 mm an. Diese passen perfekt in die Anschluss-Buchse des Hörers, die du vorher vorsichtig von ihren Anschlüssen im Telefon ziehen kannst. Stecke nun die beiden Flachstecker zum gelben und grünen Kabel in die Buchse:

Hinweis: Je nachdem, welches Telefon du verwendest, können sich die Farben der Kabel, die zum Lautsprecher im Hörer führen, natürlich unterscheiden. Schraube in diesem Fall den Telefonhörer auf und schaue kurz nach, welche Kabel du mit dem Klinkenstecker verbinden musst.

Damit ist dein Hörer schon einsatzbereit. Wenn du ihn schon einmal vorab testen möchtest, schließe den Klinkenstecker am Raspberry Pi an und spiele eine Audio-Datei ab. Im Telefonhörer sollte der Lautsprecher diese nun abspielen.

Der Button

Kommen wir zum mechanischen Teil der Hardware – einem Button, mit dem du das Gespräch mit ChatGPT starten kannst. Sobald der Button gedrückt (oder losgelassen wird) wartet der Raspberry Pi auf deine Frage und das weitere Programm nimmt seinen Lauf.

Für den Anschluss am Raspberry Pi benötigst du neben dem Button zwei Jumper-Kabel. Diese sollten circa 20 cm lang sein, damit du den Button an einer geeigneten Stelle im Gehäuse platzieren kannst – dazu gleich mehr. Solltest du nur kürzere Kabel zur Hand haben, kannst du diese auch zusammenstecken. Schließe den Button wie folgt an:

Ein Pin des Button ist hierbei mit Erde (GND) und der andere am Raspberry Pi mit Pin 16 (GPIO23) verbunden. Einen Pull-up- bzw. Pull-down-Widerstand benötigst du nicht, da du den internen Widerstand verwenden wirst.

Jetzt stellt sich die Frage, wohin mit dem Button im Gehäuse? Besonders praktisch wäre es, wenn er nicht extra gedrückt werden müsste, sondern betätigt wird, sobald der Hörer abgenommen wird. Hierfür kannst du den Button unter dem Gabelmechanismus platzieren, wie auf dem folgenden Bild zu sehen:

Wenn der Hörer aufliegt, wird die Telefongabel heruntergedrückt – der Mechanismus drückt dann den Button herunter. Sobald du den Hörer abnimmst, springt der Mechanismus hoch und lässt dadurch auch den Button los – und das Python Script wird gestartet bzw. das Gespräch kann losgehen. Das funktioniert aber nur, wenn der Button leichtgängig genug ist, damit das Gewicht des Hörers ausreicht, um ihn herunterzudrücken. Hier ist etwas Ausprobieren deinerseits gefragt. Solltest du jedoch keinen passenden Button finden, muss es doch andersherum funktionieren: Du nimmst zuerst den Hörer ab und drückst die Telefongabel manuell herunter, um das Script zu starten.

Und das war es auch schon auf der Hardware-Seite! Warte allerdings noch mit dem Einbau des Raspberry Pi und seiner Peripherie bis du das Projekt zum Laufen gebracht hast. So kannst leichter Maus, Tastatur und Bildschirm anschließen und dich um die Software kümmern.

Einrichten der Software

Kommen wir zum Kern dieses Projekts – der Software. Auf dem Raspberry Pi wirst du ein Python Script erstellen und speichern, das in einem Endlos-Loop läuft und auf dein Signal (den Button) wartet. Außerdem benötigst du einige MP3s, die du als Ansagen und für Fehlermeldungen verwendest. Das Telefon hat kein Display und keine Kontrollleuchten, deshalb läuft die “Benutzeroberfläche” über den Lautsprecher im Hörer.

Doch zunächst musst du das Betriebssystem für den Raspberry Pi vorbereiten. Hier benötigst du die Möglichkeit, per SSH auf den kleinen Rechner zugreifen zu können, da du später keinen Monitor mehr zur Verfügung haben wirst, über den du das Script starten kannst.

Falls auf deinem Raspberry Pi schon ein Betriebssystem läuft, SSH aber noch nicht aktiviert ist, hole das über die Einstellungen nach. Rufe hierfür in der Kommandozeile die Konfiguration auf:

sudo raspi-config

Anschließend wählst du den Menüpunkt Interfacing Options / SSH und aktivierst SSH. Falls du ein frisches Betriebssystem verwenden möchtest – in diesem Tutorial erkläre ich, wie du SSH direkt beim Erstellen der SD-Karte aktivieren kannst. Dort erfährst du auch, wie du per SSH von einem anderen Computer auf den Raspberry PI zugreifen kannst.

Die benötigten Python-Bibliotheken

Bevor du mit dem Python Script loslegen kannst, musst du ein paar Bibliotheken installieren, die du später brauchen wirst. Rufe hierfür auf dem Raspberry Pi die Kommandozeile auf und gib nacheinander die folgenden Befehle ein:

pip install speechRecognition
pip install openai
pip install gtts
pip install pygame

sudo apt install python3-pip flac ffmpeg -y
sudo apt install python3-pyaudio

sudo apt-get install rpi.gpio

Hierbei handelt es sich um Bibliotheken für die folgenden Funktionen:

  • speechRecognition: Stellt Funktionen bereit, um deine Stimme in Text umwandeln zu können
  • openai: Stellt die Verbindung zu ChatGPT her
  • gtts: Wandelt die Antwort von ChatGPT wieder in gesprochene Sprache um
  • pygame, flac ffmpeg, pyaudio: Benötigst du für die Verarbeitung der Audio-Dateien
  • rpi.gpio: Sorgt dafür, dass du die Pins des Raspberry Pi ansteuern kannst

Erstelle ein Konto bei OpenAI und einen API-Key

Um die API von ChatGPT nutzen zu können, benötigst du ein Konto bei OpenAI. Die API ist kostenpflichtig – aber keine Sorge, hierfür fallen keine horrenden Beträge an. Eine Antwort auf eine Frage zu bekommen, die du über das Telefon stellt, dürfte dich in der Regel nur einen Bruchteil eines Cents kosten. Selbst wenn du GPT-4 verwendest (wie, erfährst du gleich), dürften sich die Kosten in Grenzen halten.

In diesem Tutorial bei Pollux Labs erfährst du, wie du ein Konto bei OpenAI anlegst und dir einen API-Key erstellst. Dort findest du auch weitere Informationen zu den Preisen sowie einen Link zur aktuellen Preisliste. Sobald du einen API-Key und etwas Guthaben bei OpenAI besitzt, kann es direkt mit dem Python-Script weitergehen.

Das Python-Script

Um das Script zu erstellen, öffne auf deinem Raspberry Pi einen Editor (z.B. Thonny), erstelle ein neues Projekt und kopiere den folgenden Code hinein:

import speech_recognition as sr
from openai import OpenAI
import json
from gtts import gTTS
import pygame
import random
import time
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)
buttonPin = 16
GPIO.setup(buttonPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

client = OpenAI(
  api_key="DEIN API KEY",
)

def callGPT():
    #Zufällige Ansagen erzeugen
    randQuestion = random.randrange(1,5)
    randWait = random.randrange(1,4)

    #Ansage abspielen
    time.sleep(2) #2 Sekunden warten, bis die Frage kommt (Zeit, den Hörer abzunehmen)
    pygame.mixer.init()
    pygame.mixer.music.load("Question{}.mp3".format(randQuestion))
    pygame.mixer.music.play()
    while pygame.mixer.music.get_busy():
        pass 
        
    #Gesprochene Frage vom Mikrofon empfangen
    r = sr.Recognizer()
    with sr.Microphone() as source:
        print("Stelle deine Frage.")
        audio = r.listen(source)

    #Frage transkribieren
    try:
        recognizedText = r.recognize_google(audio, language = "de_DE")
        print("Google Speech Recognition hat folgendes verstanden: " + recognizedText)
    except sr.UnknownValueError:
        print("Google Speech Recognition konnte dich nicht verstehen")
        pygame.mixer.init()
        pygame.mixer.music.load("unintelligible.mp3")
        pygame.mixer.music.play()
        while pygame.mixer.music.get_busy():
            pass 
        return
    except sr.RequestError as e:
        print("Konnte kein Ergebnis von Google Speech Recognition empfangen; {0}".format(e))
        pygame.mixer.init()
        pygame.mixer.music.load("error.mp3")
        pygame.mixer.music.play()
        while pygame.mixer.music.get_busy():
            pass 
        return


    #Ansage "Bitte warten" abspielen
    pygame.mixer.init()
    pygame.mixer.music.load("Wait{}.mp3".format(randQuestion))
    pygame.mixer.music.play()
    while pygame.mixer.music.get_busy():
        pass 

    #ChatGPT
    print("Schreibe die Antwort...")
    completion = client.chat.completions.create(
    #model="gpt-3.5-turbo", 
    model="gpt-4",
    messages=[
    {"role": "system", "content": "Du beantwortest Fragen von Nutzern."},
    {"role": "user", "content": "Beantworte die folgende Frage: {}".format(recognizedText)}]
    )

    text = completion.choices[0].message.content

    #Erstellen des gTTS-Objekts
    tts = gTTS(text, lang='de')

    #Speichern der Antwort als MP3 (alte Antworten werden überschrieben)
    tts.save("output.mp3")

    #Antwort abspielen
    pygame.mixer.init()
    pygame.mixer.music.load("output.mp3")
    pygame.mixer.music.play()
    while pygame.mixer.music.get_busy():
        pass 


while True:
    buttonState = GPIO.input(buttonPin)
    if buttonState == GPIO.LOW: #LOW = Funktionsstart bei gedrücktem Button, HIGH = Start bei Loslassen
        callGPT()
    else:
        print(".")

Speichere das Script gleich ab mit einem Namen deiner Wahl – meines heißt runcallgpt.py

Trage nun deinen API-Key von OpenAI in das Script ein:

API_KEY = "DEIN API-KEY VON OPENAI"

Für einen ersten Tests des Python-Scripts benötigst du noch ein paar MP3-Dateien, die als Ansagen und Fehlermeldungen dienen. Du kannst hierfür dieses ZIP-Archiv von mir herunterladen und verwenden. Darin findest du verschiedene MP3s sowie ein kleines Script, mit dem du deine eigenen Ansagen erstellen kannst. Entpacke die Dateien in dasselbe Verzeichnis, in dem auch dein Python-Script liegt. Falls du eigene Ansagen verwendest, achte auf die Dateinamen – diese müssen mit den Dateinamen im Script übereinstimmen.

Dein erster Test

Wenn du die obigen Vorbereitungen abgeschlossen hast und alle Kabel an ihrem Platz sind, kann es losgehen! Starte das Script, nimm den Hörer ans Ohr und drücke den Button – nach 2 Sekunden sollte dich eine nette Computer-Stimme begrüßen und dich um deine Frage bitten.

Nachdem du gesprochen hast, sollte deine Frage als Text in der Konsole von Thonny erscheinen. Nach einer weiteren Ansage sollte dir die Antwort von ChatGPT vorgelesen werden.

Wie du siehst, sind das eine Menge “sollte” – falls nichts passiert oder das Script an einer bestimmten Stelle abbricht, gehe die einzelnen Stationen darin durch:

  • Ist der Button richtig angeschlossen?
  • Funktioniert das Mikrofon?

Im Script wird das “Default-Mikrofon” verwendet, der Raspberry Pi hat aber kein solches. Oft findet er es trotzdem – falls nicht, lasse dir mit folgendem Befehl die Liste der erkannten Geräte anzeigen:

sr.Microphone.list_microphone_names()

Wenn dort dein Mikro zum Beispiel an dritter Stelle auftaucht, ersetze im obigen Script die Zeile

with sr.Microphone() as source:

durch die folgende:

with sr.Microphone(device_index=3) as source:
  • Stimmt der API-KEY von OpenAI?
  • Stimmen die Dateinamen der Ansage-MP3s mit dem Script überein?

Falls dein Fehler nicht dabei ist, kopiere dir die Fehlermeldung und starte eine Google-Suche. Oft wirst du damit am schnellsten eine Lösung für dein spezifisches Problem finden.

ChatGPT-3.5 vs. ChatGPT-4

Im Script gibt es eine Stelle, an der du entscheiden kannst, ob du das Sprachmodell mit der Version 3.5 oder lieber die aktuellere Version 4 verwenden möchtest. Die beiden unterscheiden sich in ihrer Leistung und damit in der Qualität der Antworten – dafür ist die Version 4 aber auch gut 20 Mal so teuer wie ihre Vorgängerin. Genauere Zahlen findest du in der oben erwähnten Preisliste von OpenAI.

Hier kannst du einstellen, welche Version du verwendest. Kommentiere einfach die Zeile mit der nicht zu verwendenden Version aus:

#model="gpt-3.5-turbo", 
model="gpt-4",

Funktioniert? DAnn baue das Telefon zusammen

Wenn du alles funktioniert wie es soll, kannst du die Peripherie vom Raspberry Pi trennen (bis auf das Mikrofon und das Kabel zum Telefonhörer) und dich um dein Einbau kümmern. Nimm hierfür am besten die Wählscheibe heraus und verstaue alles, ohne Kabel zu knicken oder Steckverbindungen zu lösen. Achte darauf, den Button unter den Mechanismus der Telefongabel zu verstauen, ohne das er wegrutschen kann.

Wenn alles sicher untergebracht ist, kannst du die Wählscheibe wieder in die Halterung setzen, das Netzteil des Raspberry Pi aus dem Gehäuse führen und den Deckel des Telefons aufsetzen.

Das Python-Script aus der Ferne starten

Wenn du das Telefon zusammengebaut hast, hast du natürlich keine Maus, Tastatur und keinen Bildschirm mehr für den Raspberry Pi zur Verfügung. Hier kommt nun SSH ins Spiel. In diesem Projekt bei Pollux Labs wird beschrieben, wie du per SSH von einem anderen Computer auf den Raspberry Pi zugreifen kannst.

Sobald die Verbindung steht, steuere den Ordner an, in dem das Script und die MP3 liegen. In meinem Beispiel liegen die Dateien im Ordner Desktop/callGPT:

cd Desktop/callGPT

Starte anschließend das Script, in meinem Fall:

python runcallgpt.py

Nun sollte das Script laufen. Löse am Telefon den Button aus und lausche der Stimme aus dem Hörer. 🙂

Autostart

Um das Script mit dem Boot des Raspberry Pi zu starten, lege zunächst einen CronJob an. Gib hierfür in die Kommandozeile folgenden Befehl ein:

sudo nano /etc/rc.local

Ergänze anschließend ganz unten die folgenden Zeilen. Wobei du natürlich den Pfad zu deinem Script anpassen musst.

sleep 5
su - pi -c 'python /home/pi/Desktop/callGPT/runcallgpt.py' &

Eine weitere Anpassung musst du im Script nun noch machen: Ergänze zu jeder MP3, die abgespielt werden soll, noch den vollständigen Pfad – ansonsten werden sie nicht gefunden. In meinem Beispiel also:

pygame.mixer.music.load("/home/pi/Desktop/callGPT/unintelligible.mp3")

So geht es weiter

Wenn dein KI-Telefon funktioniert – herzlichen Glückwunsch! Allerdings gibt es noch eine Vielzahl von Optimierungen, die es es noch besser machen würden. Wie wäre es z.B. mit einer “besseren” Stimme? In diesem Tutorial lernst du eine weitere Methode für Text to Speech kennen.

Aktuell kannst du das Vorlesen einer Antwort mit dem obigen Script nicht unterbrechen, indem du z.B. die Gabel herunterdrückst. Auch Folgefragen zu stellen, ist noch nicht möglich. Und sicherlich gibt es noch weitere Ideen, die dieses Gadget besser machen würden. Ich werde dieses Tutorial um weitere Versionen ergänzen – falls du Lösungen und Ideen gefunden hast, schreibe sie gerne in die Kommentare.

]]>
https://polluxlabs.net/raspberry-pi-projekte/chatgpt-im-telefon-ein-retro-sprachassistent/feed/ 0
ESP8266 Wetterstation mit Datenaufzeichnung und -visualisierung https://polluxlabs.net/esp8266-projekte/esp8266-wetterstation-mit-datenaufzeichnung/ https://polluxlabs.net/esp8266-projekte/esp8266-wetterstation-mit-datenaufzeichnung/#respond Mon, 07 Aug 2023 12:54:39 +0000 https://polluxlabs.net/?p=13977 ESP8266 Wetterstation mit Datenaufzeichnung und -visualisierung Weiterlesen »

]]>
Baue eine ESP8266 Wetterstation, die dir die aktuelle Temperatur, Luftfeuchtigkeit und den Luftdruck anzeigt sowie deine Daten speichert und visualisiert. Die aktuellen Messdaten erscheinen auf einem kleinen OLED-Display. Aber das ist nicht alles: Deine Messdaten speicherst du in einer Datenbank, um auf vergangene Messungen zugreifen und sie auswerten zu können.

Um deine Messdaten zu speichern, verwendest du die Datenbank InfluxDB, die sicher hervorragend dazu eignet, zeitgebundene Daten zu speichern und nebenbei auch noch die Visualisierung deiner Daten einfach und intuitiv ermöglicht. InfluxDB wird lokal auf einem Raspberry Pi laufen. Der ESP8266 sendet die Messdaten dorthin und InfluxDB speichert und visualisiert sie. Anzeigen lassen kannst du diese dir dann in einem Browser und auf einem Gerät deiner Wahl.

Die Software, die du für die ESP8266 Wetterstation benötigst, ist kostenlos verfügbar.

Inhalte dieses Projekts:

Diese Bauteile benötigst du für die ESP8266 Wetterstation:

Den DHT22 AM ESP8266 anschließen und verwenden

Der Temperatursensor DHT22 misst neben der Temperatur auch die Luftfeuchtigkeit. Um ihn an deinem ESP8266 anzuschließen, orientiere dich an der folgenden Skizze. Achte bitte auf den 10 kΩ Widerstand, den du zwischen dem Anschluss des DHT22 am Pin D4 des ESP8266 und Plus einsetzen musst.

Temperatursensor DHT22 am ESP8266

Übrigens: Falls du deinen ESP8266 noch nicht in der Arduino IDE verfügbar gemacht hast, findest du hier bei uns ein passendes Tutorial.

DIE PASSENDEN BIBLIOTHEKEN

Um deinen Sensor verwenden zu können, musst du zwei Bibliotheken installieren, von denen du jedoch nur eine im Sketch einbinden musst. Öffne deinen Bibliotheksmanager. Suche dort zunächst nach Adafruit Unified Sensor und installiere die aktuelle Version. Die Versionsnummern in den folgenden Screenshots können abweichen.

Arduino Bibliothek Adafruit Unified Sensor

Suche anschließend nach DHT sensor library und installiere die entsprechende Bibliothek.

Arduino Bibliothek DHT Sensor Library

DIE TEMPERATUR und Luftfeuchtigkeit MESSEN

Kopiere dir den folgenden Sketch und lade ihn auf deinen Arduino hoch:

#include "DHT.h"

#define DHTPIN D4
#define DHTTYPE DHT22

float tempDHT22;
float humidity;

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(115200);
  dht.begin();
}

void loop() {

  tempDHT22 = dht.readTemperature();
  humidity = dht.readHumidity();

  Serial.print("Temperatur: ");
  Serial.print(tempDHT22);
  Serial.println("*C");
  
  Serial.print("Luftfeuchtigkeit: ");
  Serial.print(humidity);
  Serial.println("%");
  
  Serial.println();
  delay(2000);
}

So funktioniert der Sketch: Nachdem du die Bibliothek eingebunden hast, legst du den Pin fest, an dem der Sensor angeschlossen ist. In unserem Sketch ist das der Pin D4.

In der nächsten Zeile legst du das Modell des Sensors fest – in unserem Fall also ein DHT22. Anschließend erstellst du ein Objekt der Bibliothek names dht, das später bei der Messung mit den Funktionen dht.readTemperature() und dht.readHumidity() zum Einsatz kommt.

Der Rest des Sketchs dürfte für dich kein Problem sein. Achte jedoch darauf, dass die Baudrate von Sketch und Seriellem Monitor übereinstimmt. Hinweis: Es kann auch sein, dass dein DHT22 auch ohne Widerstand funktioniert – oder auch nur ohne Widerstand.

Wenn du doch lieber den Sensor DHT11 verwenden möchtest, musst du nur eine Stelle im Sketch anpassen:

#define DHTTYPE DHT11

Wie du den “kleinen Bruder” des DHT22 – also den DHT11 – anschließt, erfährst du in der folgenden Skizze. Im weiteren Verlauf des Projekts verwenden wir jedoch weiterhin den DHT22.

Temperatursensor DHT11 am ESP8266

DEN BMP180 am ESP8266 anschließen und verwenden

Neben der Luftfeuchtigkeit und der Temperatur soll die ESP8266 Wetterstation auch den aktuellen Luftdruck messen. Hierfür eignet sich der Sensor BMP180. Da dieser auch die Temperatur messen kann, schauen wir uns gleich auch noch die Messunterschiede zwischen DHT11 und BMP180 an. Doch zunächst zum Anschluss – orientiere dich hierbei an der folgenden Skizze:

Sensoren DHT22 und BMP180 am ESP8266

Die benötigte Bibliothek

Neben der bereits vorinstallierten Bibliothek Wire (für die Kommunikation per I²C), benötigst du noch eine weitere, um die Daten des Sensors problemlos auslesen zu können.

Öffne also den Bibliotheksmanager in der Arduino IDE und suche nach BMP180. Du findest nun eine Bibliothek namens Adafruit BMP085 Library – das ist die richtige, auch wenn sie ein anderes Modell im Namen trägt. Der BMP085 war das Vorgängermodell des BMP180, was die Kommunikation angeht, jedoch mehr oder weniger baugleich.

Adafruit BMP085 Bibliothek

Die Temperatur und den Luftdruck messen

Nun erweiterst du den obigen Sketch um den Code für den BMP180:

#include "DHT.h"
#include "Wire.h"
#include "Adafruit_BMP085.h"

#define DHTPIN D4
#define DHTTYPE DHT22

Adafruit_BMP085 bmp;

float tempDHT22;
float tempBMP180;
float humidity;
float pressure;

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(115200);
  dht.begin();

  if (!bmp.begin()) {
    Serial.println("Sensor BMP180 nicht gefunden!");
    while (1) {}
  }
}

void loop() {

  tempDHT22 = dht.readTemperature();
  tempBMP180 = bmp.readTemperature();
  humidity = dht.readHumidity();
  pressure = bmp.readPressure();

  Serial.print("Temperatur DHT22: ");
  Serial.print(tempDHT22);
  Serial.println("*C");

  Serial.print("Temperatur BMP180: ");
  Serial.print(tempBMP180);
  Serial.println("*C");

  Serial.print("Luftfeuchtigkeit DHT22: ");
  Serial.print(humidity);
  Serial.println("%");

  Serial.print("Luftdruck BMP180: ");
  Serial.print(pressure/100);
  Serial.println("hPa");

  Serial.println();
  delay(2000);
}

Sobald du den Sketch auf deinen ESP8266 geladen hast, sollten im seriellen Monitor die vier Messdaten erscheinen.

Ausgabe der Wetterdaten im seriellen Monitor

In den allermeisten Fällen dürfte die Temperaturmessung des DHT22 und des BMP180 etwas auseinander liegen. Ein Blick in die jeweiligen Datenblätter verrät, dass beide Sensoren eine Genauigkeit von ±0,5°C haben – das ist eigentlich schon recht genau und für eine Wetterstation sicherlich genau genug. Für welchen Messwert du dich entscheidest, liegt nun bei dir – vielleicht hast du noch ein weiteres Thermometer zur Hand, das du als Referenz einsetzen kannst.

Im weiteren Verlauf dieses Projekts verwenden wir die Temperaturdaten des BMP180.

Das OLED-Display anschließen

Aktuell siehst du deine Messdaten nur im seriellen Monitor. Deshalb kommt nun ein Display zum Einsatz, auf dem du sie bequemer ablesen kannst. In diesem Projekt verwenden wir das handelsübliche OLED-Display Adafruit SSD1306 mit einer Größe von 128×64 px.

Erweitere also den Aufbau auf deinem Breadboard wie folgt:

Die fertig aufgebaute ESP8266 Wetterstation

Wie du siehst, sind sowohl der BMP180 als auch das OLED-Display per I²C (also an den Pins D1 und D2) am ESP8266 angeschlossen. Damit der Microcontroller beide Bauteile ansprechen kann, besitzen sie unterschiedliche Adressen – das OLED-Display mit 128×64 px die Adresse 0x3C und der BMP180 die Adresse 0x77.

Die benötigten Bibliotheken

Nun ist deine ESP8266 Wetterstation vollständig. Allerdings fehlt noch der Sketch, mit dem du deine Messdaten auf dem OLED-Display anzeigst. Auch für das Display benötigst du die Unterstützung von Bibliotheken, die du im Handumdrehen installierst. Öffne also wieder den Bibliotheksmanager und suche zunächst nach Adafruit SSD1306 und installiere die neueste Version. Falls du gefragt wirst, ob du auch zugehörige Erweiterungen installieren möchtest, bestätige das mit einem Ja.

Die zweite Bibliothek findest du mit einer Suche nach Adafruit GFX Library. Falls diese schon im Zuge der ersten Installation mitinstalliert wurde, brauchst du nichts weiter zu tun und kannst den Bibliotheksmanager schließen.

___STEADY_PAYWALL___

Messdaten auf dem Display anzeigen

Ersetze den Code auf deinem ESP8266 durch den folgenden erweiterten Sketch:

#include "DHT.h"
#include "Wire.h"
#include "Adafruit_BMP085.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define DHTPIN D4
#define DHTTYPE DHT22

Adafruit_BMP085 bmp;

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

float tempDHT22;
float tempBMP180;
float humidity;
float pressure;

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(115200);
  dht.begin();

  if (!bmp.begin()) {
    Serial.println("Sensor BMP180 nicht gefunden!");
    while (1) {}
  }

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // Display-Addresse: 0x3C für Groesse 128x64px
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }
  display.setTextSize(1);       //Schriftgröße
  display.setTextColor(WHITE);  //Schriftfarbe


  display.clearDisplay();
  display.display();
}

void loop() {

  tempDHT22 = dht.readTemperature();
  tempBMP180 = bmp.readTemperature();
  humidity = dht.readHumidity();
  pressure = bmp.readPressure();

  Serial.print("Temperatur DHT22: ");
  Serial.print(tempDHT22);
  Serial.println("*C");

  Serial.print("Temperatur BMP180: ");
  Serial.print(tempBMP180);
  Serial.println("*C");

  Serial.print("Luftfeuchtigkeit DHT22: ");
  Serial.print(humidity);
  Serial.println("%");

  Serial.print("Luftdruck BMP180: ");
  Serial.print(pressure / 100);
  Serial.println("hPa");
  Serial.println();

  display.clearDisplay();

  display.setCursor(10, 10);
  display.println("Temp.:  " + String(tempBMP180) + " *C");

  display.setCursor(10, 29);
  display.println("Luftf.: " + String(humidity) + " %");
  display.setCursor(10, 48);

  display.println("Luftd.: " + String(pressure/100) + " hPa");

  display.display();
  delay(2000);
}

Auf deinem OLED-Display sollten nun untereinander die Werte für Temperatur, Luftfeuchtigkeit und Luftdruck zu sehen sein, die alle zwei Sekunden aktualisiert werden.

Die Daten auf dem Raspberry Pi speichern und visualisieren

Messdaten auf einem Display sind eine tolle Sache – aber wenn du die Werte deiner Wetterstation über längere Zeit speichern und sie als Graphen anzeigen möchtest, musst du ein paar Schritte weiter gehen.

Im Folgenden richtest du deinen Raspberry Pi so ein, dass du ihn bequem von deinem Computer per SSH steuern kannst. Anschließend installierst du dort die Datenbank InfluxDB, in der deine Messwerte gespeichert werden. Praktischerweise bringt InfluxDB gleich eine Möglichkeit, die Daten ansprechend darzustellen. Doch eins nach dem anderen.

Das Betriebssystem auf dem Raspberry Pi installieren

Zunächst benötigst du ein entsprechend konfiguriertes Betriebssystem. Das lässt du auf eine Micro-SD-Karte schreiben, die du dann in deinen Raspberry Pi steckst.

Besonders einfach ist das mit dem kostenlosen Rasperry Pi Imager, den du hier herunterladen kannst. Wähle einfach die Version für dein Betriebssystem und starte den Download.

Download-Screen Raspberry Pi Imager

Öffne das Programm nach dem Download und wähle in der Oberfläche unter Betriebssystem -> Raspberry Pi OS (other) -> Raspberry Pi OS (64-bit).

Schließe als nächstes die Micro-SD-Karte, auf die du das Raspberry Pi OS installieren möchtest, an deinen Computer an. Wähle sie anschließend im Feld SD-Karte aus.

Bevor du jetzt auf den Button Schreiben klickst, wähle zunächst die erweiterten Einstellungen hinter dem Zahnrad-Symbol. Hier kannst du gleich den Hostnamen festlegen, SSH aktivieren und auch deine WLAN-Zugangsdaten hinterlegen. Das bedeutet, dass du deinen Raspberry Pi nicht mehr an einen Monitor anschließen musst, um diese Einstellungen vorzunehmen. Später reicht es, die Micro-SD-Karte und das Stromkabel einzustecken.

Wähle zunächst im oberen Bereich die folgenden Optionen:

Raspberry Pi Imager erweiterte Optionen

Anschließend legst du deinen Benutzernamen fest und hinterlegst deine WLAN-Zugangsdaten. Die Wahl deines Benutzernamen steht dir natürlich frei – in den folgenden Befehlen verwenden wir hier jedoch das gängige pi.

Raspberry Pi Imager erweiterte Optionen

Noch ein Stück weiter unten stellst du noch dein Land und deine Zeitzone ein:

Raspberry Pi Imager erweiterte Optionen

Und das war es. Speichere diese Einstellungen und klicke im Hauptmenü auf Schreiben.

Per SSH auf den Raspberry Pi zugreifen

Nachdem das Raspberry Pi OS auf der Micro-SD-Karte und schließlich in deinem Raspberry Pi gelandet ist, starte diesen, indem du das Stromkabel anschließt. Warte nun ein paar Minuten, bis er fertig gebootet hat.

Nun wirst du dich per SSH (Secure Shell) mit dem Raspberry Pi verbinden. SHH ist eine beliebte – und verschlüsselte – Verbindung zwischen zwei Geräten. Hierdurch kannst du auf sichere Art und Weise von deinem Computer auf den Raspberry Pi zugreifen, Software installieren – und später deine Wetterdaten auswerten. So richtest du die Verbindung ein:

MacOS & Linux

Bei diesen beiden Betriebssystemen brauchst du lediglich das Terminal. In Unix-basierten Betriebssystemen ist SSH nämlich schon vorinstalliert. Öffne also das Terminal und tippe den folgenden Befehl ein:

sudo ssh pi@raspberrypi.local

Hinweis: Falls du einen anderen Benutzer- und Hostnamen vergeben hast, passe den Befehl entsprechend an.

Wenn du nach deinem Passwort gefragt wirst, trage jenes, das du im Raspberry Pi Imager vergeben hast, ein und drücke Enter. Möglicherweise musst du auch noch mal mit einem yes bestätigen, dass du die Verbindung aufbauen möchtest. Wenn die Verbindung steht, siehst du die folgende Zeile in deinem Terminal:

Kommandozeile im Terminal

Um die Verbindung wieder zu beenden und deinen Raspberry Pi herunterzufahren, trage folgenden Befehl ins Terminal ein:

sudo poweroff

Windows

In Windows benötigst du eine Software, um dich per SSH zu verbinden – zum Beispiel PuTTY. Diese Programm kannst du hier herunterladen.

Installiere PuTTY auf deinem Computer, öffne es und trage die folgenden Daten in den Optionen/Einstellungen ein:

  • Host Name: raspberrypi
  • Port: 22
  • Connection type: SSH

Klicke anschließend auf Open/Öffnen. Bei der ersten Verbindung erscheint ein Dialog-Fenster, das dich davor warnt, dass du eine Verbindung zu einem unbekannten Host aufbaust. Diese kannst du mit einem Klick auf No schließen.

Logge dich als nächstes mit deinen Zugangsdaten, die du im Raspberry Pi Imager festgelegt hast, ein. Sobald die Verbindung steht, siehst du auch wieder die oben genannte Zeile. Um den Raspberry Pi auszuschalten, verwendest du ebenfalls.

sudo poweroff

INfluxDB 2 auf dem Raspberry Pi installieren

Jetzt wo deine Verbindung steht, kannst du die Datenbank InfluxDB installieren, um die Daten deiner ESP8266 Wetterstation zu speichern. Trage hierfür im Terminal bzw. in PuTTY den folgenden Befehl ein. Kopiere die folgenden Zeilen vollständig, füge sie ins Terminal ein und führe sie mit Enter aus.

wget -q https://repos.influxdata.com/influxdata-archive_compat.key
echo '393e8779c89ac8d958f81f942f9ad7fb82a25e133faddaf92e15b16e6ac9ce4c influxdata-archive_compat.key' | sha256sum -c && cat influxdata-archive_compat.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg > /dev/null
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg] https://repos.influxdata.com/debian stable main' | sudo tee /etc/apt/sources.list.d/influxdata.list

sudo apt-get update && sudo apt-get install influxdb2

Nun läuft die Installation im Terminal. Ab und an wirst du um deine Zustimmung zur Installation von Erweiterungspaketen gefragt. Gib diese einfach durch Eingabe von Y und Enter.

Nachdem die Installation abgeschlossen ist, erscheint wieder die Zeile pi@raspberrypi:~$

Trage nun folgenden Befehl im Terminal ein, der dafür sorgt, dass InfluxDB beim Start des Raspberry Pi ebenfalls gestartet wird:

sudo service influxdb start

Anschließend prüfst du noch kurz, ob InfluxDB aktiv ist:

sudo service influxdb status

Im Terminal erscheint nun einiges an Text – darunter hoffentlich ein grünes active (running):

Ausgabe im Terminal, dass InfluxDB aktiv ist

Drücke q auf deiner Tastatur, um wieder zur Kommandozeile zurückzukehren.

Auf die Datenbank zugreifen

Um auf InfluxDB im Browser zugreifen zu können, benötigst du zunächst die IP-Adresse des Raspberry Pi. Diese findest du mit folgendem Befehl im Terminal heraus:

hostname -I

In der Antwort des Raspberry Pi siehst du ganz vorne die IP-Adresse, in unserem Fall die 192.168.0.129

Um nun InfluxDB zu starten, öffne in deinem Browser ein neues Tab oder Fenster und tippe deine IP-Adresse gefolgt vom Port :8086. Die Adresse sieht dann zum Beispiel so aus:

192.168.0.129:8086

In deinem Browser öffnet sich nun die Startseite von InfluxDB:

Startscreen InfluxDB auf dem Raspberry Pi

Nun kann es losgehen – klicke also auf Get started. Es folgt eine Seite, auf der du deine Zugangsdaten festlegst:

Trage deinen Benutzernamen unter Username ein und hinterlege ein sicheres Passwort. Unter Initial Organization Name kannst du ebenso deinen Benutzernamen eintragen – solange du nicht wirklich eine Arbeitsgruppe für die Wetterstation hast.

Unter Initial Bucket Name kannst du zum Beispiel ESP8266 Wetterstation eintragen. Unter diesem Namen findest du später die Datenbank für deine Wetterstation.

Bestätige deine Eingabe und klicke auf der folgenden Seite auf Quick Start:

Testweise die WLAN-Signalstärke übertragen

Anschließend findest du auf der Webseite die Kachel Arduino. Dahinter verbirgt sich eine Art Tutorial, wie du deinen ESP8266 einrichtest, um Daten an die Datenbank zu übertragen – hier die Stärke deines WLAN-Signals. Dieses Tutorial spielen wir im Folgenden teilweise durch, um einen Sketch zu erhalten, den du danach so erweitern kannst, dass er die Messdaten der Wetterstation überträgt.

Im Menü links findest du mehrere Punkte – da du den zweiten Punkt Prepare Arduino IDE (also den ESP8266 in der IDE verfügbar machen) bereits beim Aufbau der Wetterstation abgeschlossen hast, kannst du ihn gleich überspringen und auf Install Dependencies klicken.

Hier erhältst du Informationen zu einer Bibliothek, die du in der Arduino IDE installieren musst, um Daten an InfluxDB übertragen zu können. Öffne also den Bibliotheksmanager und suche nach InfluxDB. Im Tutorial heißt die erforderliche Bibliothek InfluxDB Client for Arduino – es kann aber gut sein, dass du nur eine Bibliothek namens ESP8266 Influxdb findest. Sollte das der Fall sein, installiere einfach diese stattdessen. Wenn du fertig bist, klicke auf Next.

Auf der nächsten Seite Initialize Client kannst du den Bucket auswählen, in den die Daten übertragen werden sollen:

Gleich darunter findest du Code, den du in einen leeren Sketch einfügen sollst. Erstelle also einen neuen Sketch, kopiere den zur Verfügung gestellten Code und füge ihn ein. Achte unbedingt darauf, dass du die leeren Funktionen Setup und Loop mit diesem Code überschreibst.

In diesem Beispiel-Sketch musst du noch deine WLAN-Zugangsdaten hinterlegen, damit dein ESP8266 mit dem Raspberry Pi kommunizieren kann:

  // WiFi AP SSID
  #define WIFI_SSID "YOUR_WIFI_SSID"
  // WiFi password
  #define WIFI_PASSWORD "YOUR_WIFI_PASSWORD"

Trage also deine Daten ein und klicke im Tutorial wieder auf Next.

Es folgen wieder zwei Zeilen Code, die du in der Setup-Funktion unterhalb des dort schon vorhandenen Codes einfügen musst:

Der Sketch in diesem Tutorial überträgt die Stärke deines WLAN-Signals an die Datenbank. Die zwei Zeilen fügen den Datenpunkten zwei Tags hinzu – einmal das Gerät und einmal die SSID, also der Name deines WLAN-Netzwerks.

Gleich darunter findest du den Loop des Sketchs. Dieser ist aktuell noch leer – kopiere ihn dir also aus dem Tutorial und überschreibe damit die leere Loop-Funktion in deiner Arduino IDE. Im Loop wird jede Sekunde der Received Signal Strength Indicator (RSSI, also die Signalstärke des WLAN-Netzes) gemessen und in der InfluxDB hinterlegt.

Die zwei nächsten Punkte des Tutorials können wir überspringen. Hier geht es um Datenbank-Abfragen, die wir jedoch für unsere Zwecke nicht benötigen.

Der vollständige Beispiel-Sketch

Diese Copy & Paste Arbeit hat etwas Fingerspitzengefühl erfordert. Wenn alles an der richtigen Stelle gelandet ist, sollte dein Sketch wie folgt aussehen:

#if defined(ESP32)
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
#define DEVICE "ESP32"
#elif defined(ESP8266)
#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti wifiMulti;
#define DEVICE "ESP8266"
#endif

#include <InfluxDbClient.h>
#include <InfluxDbCloud.h>

// WiFi AP SSID
#define WIFI_SSID "YOUR_WIFI_SSID"
// WiFi password
#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD"

#define INFLUXDB_URL "DEINE URL (ist vorausgefüllt)"
#define INFLUXDB_TOKEN "DEIN TOKEN (ist vorausgefüllt)"
#define INFLUXDB_ORG "DEINE ORG (ist vorausgefüllt)"
#define INFLUXDB_BUCKET "ESP8266 Wetterstation"

// Time zone info
#define TZ_INFO "UTC2"

// Declare InfluxDB client instance with preconfigured InfluxCloud certificate
InfluxDBClient client(INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN, InfluxDbCloud2CACert);

// Declare Data point
Point sensor("wifi_status");

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

  // Setup wifi
  WiFi.mode(WIFI_STA);
  wifiMulti.addAP(WIFI_SSID, WIFI_PASSWORD);

  Serial.print("Connecting to wifi");
  while (wifiMulti.run() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.println();

  // Accurate time is necessary for certificate validation and writing in batches
  // We use the NTP servers in your area as provided by: https://www.pool.ntp.org/zone/
  // Syncing progress and the time will be printed to Serial.
  timeSync(TZ_INFO, "pool.ntp.org", "time.nis.gov");


  // Check server connection
  if (client.validateConnection()) {
    Serial.print("Connected to InfluxDB: ");
    Serial.println(client.getServerUrl());
  } else {
    Serial.print("InfluxDB connection failed: ");
    Serial.println(client.getLastErrorMessage());
  }

  // Add tags to the data point
  sensor.addTag("device", DEVICE);
  sensor.addTag("SSID", WiFi.SSID());
}

void loop() {
    // Clear fields for reusing the point. Tags will remain the same as set above.
    sensor.clearFields();
  
    // Store measured value into point
    // Report RSSI of currently connected network
    sensor.addField("rssi", WiFi.RSSI());
  
    // Print what are we exactly writing
    Serial.print("Writing: ");
    Serial.println(sensor.toLineProtocol());
  
    // Check WiFi connection and reconnect if needed
    if (wifiMulti.run() != WL_CONNECTED) {
      Serial.println("Wifi connection lost");
    }
  
    // Write point
    if (!client.writePoint(sensor)) {
      Serial.print("InfluxDB write failed: ");
      Serial.println(client.getLastErrorMessage());
    }
  
    Serial.println("Waiting 1 second");
    delay(1000);
    
    }

Die Daten visualisieren

Jetzt wird es Zeit, sich die übertragenen Daten einmal anzusehen. Öffne hierfür im Menü links den Data Explorer über das Symbol mit dem Koordinatensystem und dem Graphen.

Hier kannst du dir im unteren Bereich eine Abfrage (Query) basteln, die die Signalstärke als Graphen darstellt:

Wähle hierfür links deinen Bucket und im ersten Filter rechts daneben im Dropdown-Menü SSID und darunter den Wert, der dem Namen deines WLAN-Netzwerks entspricht. Rechts daneben sollte ein weiterer Filter aufgehen, in dem im Dropdown-Menü bereits _field vorausgewählt ist. Als einzigen Wert findest du darin rssi, also die Signalstärke. Wähle diesen Wert aus. Falls noch ein weiterer Filter rechts daneben aufgehen, kannst du diesen über das X in der Ecke schließen.

Um diese als Graphen anzuzeigen, musst du nur noch rechts auf Submit klicken. Nun sollte im oberen Bereich eine Graph entstehen – hier ein Beispiel, das schon einige Minuten lief:

Deine Abfrage kannst du natürlich zeitlich anpassen. Voreingestellt ist ein Zeitraum von einer Stunde (Past 1h). Über das entsprechende Dropdown kannst du auch andere Zeiträume einstellen.

Ganz oben findest du auch die Auswahl Custom Time Range – hierüber kannst du den Zeitraum noch genauer über einen Kalender einstellen. Sobald du deine Auswahl getroffen hast, klicke links neben dem Dropdown-Menü auf den Refresh-Button oder auf Submit. Über diese beiden Buttons kannst du den Graphen auch aktualisieren, um die aktuellen Daten miteinzubeziehen.

Du überträgst nun bereits Daten von deinem ESP8266 an deine Datenbank auf dem Raspberry Pi – wenn auch “nur” die Stärke des WLAN-Signals. Nun wird es Zeit, die Messdaten zu übertragen.

Die Daten der ESP8266 WEtterstation übertragen und anzeigen

Um statt des WLAN-Signals die Messdaten zu übertragen, fügst du den Sketch aus dem Tutorial und deinen bisherigen Sketch der Wetterstation zusammen – und änderst außerdem noch zwei kleinere Stellen im Code.

Kopiere hierfür Stück für Stück aus deinem Wetterstation-Sketch die verschiedenen Teile (Bibliotheken und Definitionen, Setup sowie Loop) und füge sie nacheinander in den Sketch des Tutorials ein. Wenn du das getan hast, sieht dein vollständiger Sketch wie folgt aus:

#if defined(ESP32)
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
#define DEVICE "ESP32"
#elif defined(ESP8266)
#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti wifiMulti;
#define DEVICE "ESP8266"
#endif

#include <InfluxDbClient.h>
#include <InfluxDbCloud.h>

// WiFi AP SSID
#define WIFI_SSID "YOUR_WIFI_SSID"
// WiFi password
#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD"

#define INFLUXDB_URL "DEINE URL (ist vorausgefüllt)"
#define INFLUXDB_TOKEN "DEIN TOKEN (ist vorausgefüllt)"
#define INFLUXDB_ORG "DEINE ORG (ist vorausgefüllt)"
#define INFLUXDB_BUCKET "ESP8266 Wetterstation"

// Time zone info
#define TZ_INFO "UTC2"

// Declare InfluxDB client instance with preconfigured InfluxCloud certificate
InfluxDBClient client(INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN, InfluxDbCloud2CACert);

// Declare Data point
Point sensor("wifi_status");

#include "DHT.h"
#include "Wire.h"
#include "Adafruit_BMP085.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define DHTPIN D4
#define DHTTYPE DHT22

Adafruit_BMP085 bmp;

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

float tempDHT22;
float tempBMP180;
float humidity;
float pressure;

DHT dht(DHTPIN, DHTTYPE);

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

  // Setup wifi
  WiFi.mode(WIFI_STA);
  wifiMulti.addAP(WIFI_SSID, WIFI_PASSWORD);

  Serial.print("Connecting to wifi");
  while (wifiMulti.run() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.println();

  // Accurate time is necessary for certificate validation and writing in batches
  // We use the NTP servers in your area as provided by: https://www.pool.ntp.org/zone/
  // Syncing progress and the time will be printed to Serial.
  timeSync(TZ_INFO, "pool.ntp.org", "time.nis.gov");


  // Check server connection
  if (client.validateConnection()) {
    Serial.print("Connected to InfluxDB: ");
    Serial.println(client.getServerUrl());
  } else {
    Serial.print("InfluxDB connection failed: ");
    Serial.println(client.getLastErrorMessage());
  }

  // Add tags to the data point
  sensor.addTag("device", DEVICE);
  
  Serial.begin(115200);
  dht.begin();

  if (!bmp.begin()) {
    Serial.println("Sensor BMP180 nicht gefunden!");
    while (1) {}
  }

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // Display-Addresse: 0x3C für Groesse 128x64px
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }
  display.setTextSize(1);       //Schriftgröße
  display.setTextColor(WHITE);  //Schriftfarbe


  display.clearDisplay();
  display.display();
}

void loop() {
  tempDHT22 = dht.readTemperature();
  tempBMP180 = bmp.readTemperature();
  humidity = dht.readHumidity();
  pressure = bmp.readPressure();

  Serial.print("Temperatur DHT22: ");
  Serial.print(tempDHT22);
  Serial.println("*C");

  Serial.print("Temperatur BMP180: ");
  Serial.print(tempBMP180);
  Serial.println("*C");

  Serial.print("Luftfeuchtigkeit DHT22: ");
  Serial.print(humidity);
  Serial.println("%");

  Serial.print("Luftdruck BMP180: ");
  Serial.print(pressure / 100);
  Serial.println("hPa");
  Serial.println();

  display.clearDisplay();
  display.setCursor(10, 10);
  display.println("Temp.:  " + String(tempBMP180) + " *C");
  display.setCursor(10, 29);
  display.println("Luftf.: " + String(humidity) + " %");
  display.setCursor(10, 48);
  display.println("Luftd.: " + String(pressure / 100) + " hPa");

  display.display();

  // Clear fields for reusing the point. Tags will remain the same as set above.
  sensor.clearFields();

  // Store measured value into point
  sensor.addField("temp", tempBMP180);
  sensor.addField("humidity", humidity);
  sensor.addField("pressure", pressure/100);

  // Print what are we exactly writing
  Serial.print("Writing: ");
  Serial.println(sensor.toLineProtocol());

  // Check WiFi connection and reconnect if needed
  if (wifiMulti.run() != WL_CONNECTED) {
    Serial.println("Wifi connection lost");
  }

  // Write point
  if (!client.writePoint(sensor)) {
    Serial.print("InfluxDB write failed: ");
    Serial.println(client.getLastErrorMessage());
  }



  Serial.println("Waiting 1 second");
  delay(1000);
}

Im Code oben findest du zwei Änderungen, die wir uns jetzt näher anschauen. Zunächst streichst du den Tag SSID in der Setup-Funktion, da wir das nicht mehr brauchen. Übrig bleibt das device (also ESP8266) – hierüber können wir später im Data Explorer die Messdaten leichter finden.

// Add tags to the data point
sensor.addTag("device", DEVICE);

Außerdem müssen statt der Signalstärke unsere Messdaten für Temperatur, Luftfeuchtigkeit und -druck übertragen werden. Das geschieht im Loop mit folgendem Code:

  // Store measured value into point
  sensor.addField("temp", tempBMP180);
  sensor.addField("humidity", humidity);
  sensor.addField("pressure", pressure/100);

Hiermit werden die drei Felder (fields) mit den Namen temp, humidity und pressure in der Datenbank angelegt und mit den aktuellen Daten der Sensoren versorgt. Diese stammen aus unseren Variablen tempBMP180, humidity und pressure. Wie du siehst teilst du den Wert in der Variablen pressure noch durch 100, um statt Pascal (Pa) Hektopascal (hPa) zu erhalten.

Und das war auch schon alles. Achte darauf, dass deine korrekten WLAN-Zugangsdaten im Sketch eingetragen sind und lade ihn auf deinen ESP8266.

Die Daten im Data Explorer anzeigen

Als nächstes schaust du im Data Explorer nach, ob die Daten auch wie gewünscht deine Datenbank erreichen. Klicke hierfür links auf den entsprechenden Menüpunkt und erstelle deine Abfrage wie folgt:

Hier pickst du dir testweise die Temperatur über die Variable temp heraus. Wenn du deine Abfrage erstellt hast, klicke auf den Button Submit. Im oberen Bereich der Seite sollte nun ein Graph zu sehen sein – hier wieder ein Beispiel, das schon einige Zeit lief:

Wie du hier siehst begann die Aufzeichnung kurz nach 12 Uhr am 7. August 2023 mit Werten um die 24 °C. Gegen 12:20 Uhr brach die Verbindung zur Datenbank ab (die Wetterstation war aus) und begann wieder gegen 12:30 Uhr. Diese Zwischenzeit wird im Graphen als Verbindungslinie zwischen dem letzten erhaltenen und ersten wieder verfügbaren Datenpunkt dargestellt. Anschließend sank die Temperatur kontinuierlich von 25 °C auf circa 24,7 °C.

Hinweis: Auf der rechten Seite findest du den Punk Aggregate Function – hier kannst du einstellen, ob du von den Temperaturwerten den Durchschnitt (mean), den Median oder immer den letzten aktuellen Wert (last) sehen möchtest. Die letzte Option zeigt dir immer den aktuellen, tatsächlichen Wert an. Die beiden anderen glätten hingegen den Graphen, sodass Temperaturausschläge weniger ins Gewicht fallen.

Solange deine ESP8266 Wetterstation also läuft, werden Daten gesammelt. Auf diese kannst du entweder über das Dropdown (z.B. Past 1h) oder spezifisch über den Punkt Custom Time Range zugreifen. Es gibt allerdings noch eine weitere spannende Funktion: ein Dashboard.

Die Messdaten auf einem Dashboard anzeigen

Um die aktuelle Abfrage auf einem Dashboard zu speichern, das dir einen schnellen Überblick verschafft, klicke zunächst im Menü links auf den entsprechenden Button:

Klicke anschließend rechts auf den Button Create Dashboard und anschließend auf New Dashboard. Auf der folgenden Seite kannst du oben einen Namen für das Dashboard vergeben, zum Beispiel ESP8266 Wetterstation:

Anschließend kannst du über den Button Add Cell eine Kachel hinzufügen – zum Beispiel den gerade gesehenen Temperaturverlauf. Du landest nach deinem Klick wieder in der Ansicht des Data Explorers, nur das du diesmal hier die Abfrage für die neue Temperatur-Kachel erstellst.

Vergib zunächst wieder ganz oben einen Namen, zum Beispiel Temperaturverlauf. Anschließend wählst du im unteren Bereich wieder die Variable temp aus.

Alles, was jetzt noch fehlt ist ein Klick auf das Häkchen oben rechts – und schon landest du wieder auf deinem Dashboard, das nun den Temperaturverlauf enthält.

Im oberen Bereich findest du den Button Set Auto Refresh – hierüber kannst du einstellen, in welchem Intervall das Dashboard und damit auch der Graph aktualisiert werden soll.

Apropos Graph, du kannst auf dem Dashboard auch die aktuelle Temperatur anzeigen lassen. Klicke hierfür wieder auf Add Cell und erstelle die bekannte Abfrage erneut. Sobald du auf Submit geklickt hast, erscheint wieder dein Temperaturverlauf. Oben links findest du ein Dropdown-Menü, in dem du die Anzeige anpassen kannst. Wähle hier den Eintrag Gauge.

Nun siehst du nicht mehr den Verlauf, sondern die aktuelle Temperatur:

Vergib dieser Kachel wieder einen Namen und klicke abschließend auf das Häkchen rechts oben. Nun landest du wieder in deinem Dashboard mit den zwei Kacheln zur Temperatur.

Mit deinen beiden anderen Messwerten zu Luftdruck und -feuchtigkeit kannst du genauso verfahren. Noch ein letzter Hinweis zu den Kacheln: Über den Button Customize kannst du die Anzeige noch anpassen, was besonders beim Luftdruck sinnvoll ist. Stelle hier unter Thresholds einen Bereich von 900 bis 1100 ein, damit der Zeiger auch etwas zum Anzeigen hat. Ebenso kannst du dem Wert noch ein Suffix vergeben – hier also hPa.

Warnungen per Nachricht senden

Möchtest du Nachrichten an dich oder jemand anderes senden, wenn ein bestimmter Messwert über- oder unterschritten wird? Im folgenden Tutorial erfährst du, wie du mit dem ESP8266 oder ESP32 Nachrichten per Telegram, WhatsApp und E-Mail verschickst.

]]>
https://polluxlabs.net/esp8266-projekte/esp8266-wetterstation-mit-datenaufzeichnung/feed/ 0
Countdown zum nächsten SpaceX Raketenstart https://polluxlabs.net/esp8266-projekte/countdown-zum-naechsten-spacex-raketenstart/ Sat, 20 Mar 2021 16:18:28 +0000 https://polluxlabs.net/?p=4485 Countdown zum nächsten SpaceX Raketenstart Weiterlesen »

]]>
Wenn du an einen Raketenstart denkst, kommt dir bestimmt vieles in den Sinn. Sicherlich auch der obligatorische Countdown. Wie wäre es, wenn du daheim die Sekunden zum nächsten Launch einer Rakete von SpaceX herunterzählst?

Elon Musks Weltraumunternehmen betreibt eine API, von der du unter anderem die Daten des nächsten Starts beziehen kannst. Hierfür benötigst du nur einen ESP8266. Den Countdown kannst du stilecht auf einem 7-Segment Display darstellen. Und für die Beleuchtung beim Lift-off eignet sich ein NeoPixel LED-Ring.

Fortgeschrittene

1 – 2 Stunden

ca. 15 €

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

Noch ein Hinweis vorab: Genau auf die Sekunde wirst den Lift-off der Rakete leider nicht treffen. Das liegt weniger an deinem Projekt, sondern daran, dass die meisten Raketen nicht ganz pünktlich sind. 😉 Zumindest haben wir bei unseren Tests beobachtet, dass der tatsächliche Start von der Angabe in der API immer um ein paar Sekunden abweicht.

Grundlegende Tutorials

In diesem Projekt kommt einiges zum Einsatz, für das du auf Pollux Labs passende Tutorials findest:

Schaue zunächst in die oben genannten Tutorials, wenn du ein paar der Themen noch nicht kennst oder erfahren möchtest, wie du ein 7-Segment Display oder den NeoPixel LED-Ring anschließt.

Im Folgenden konzentrieren wir uns auf den Start der nächsten Rakete von SpaceX – bzw. auf die Daten, die du hierfür benötigst.

Die SpaceX API

SpaceX betreibt eine sehr große Datenbank, aus der du dich kostenlos bedienen darfst. Hier findest du beispielsweise Daten zu Raketen, Modulen und Startrampen – aber natürlich auch zu vergangenen und kommenden Raketenstarts. Du findest sogar die aktuelle Position des Tesla Roadsters, der in den Orbit geschossen wurde.

SpaceX Roadster im Orbit
Foto: SpaceX

All diese Daten kannst du über eine API abrufen und weiterverarbeiten – und zwar auch mit deinem ESP8266. Für dieses Projekt benötigst du also die Daten zum nächsten Raketenstart. Diese findest du unter folgender Adresse:

https://api.spacexdata.com/v4/launches/next

Wenn du diese URL in deinem Browser aufrufst, findest eine ganze Menge Daten im JSON-Format. Unter anderem auch den Zeitpunkt des nächsten SpaceX-Starts als Unixzeit. In diesem Fall der 24.3.2021 um 9:58 MEZ.

"date_unix":1616576280

Diese URL verwendest du in deinem Sketch, um mit Hilfe der aktuellen Uhrzeit die Dauer bis zum nächsten Start – und somit den Countdown – zu berechnen.

Die Daten abrufen

Du findest den gesamten Sketch am Ende des Projekts. An dieser Stelle steigen wir direkt beim Abruf der Daten von der SpaceX API ein.

Hierfür hinterlegst du zunächst zu Beginn des Sketchs die URL der API und den Port, der für die sichere Übertragung via HTTPS zuständig ist: 443.

const char* host = "https://api.spacexdata.com/v4/launches/next";
const int httpsPort = 443;

Als nächstes kommt der Loop. Hier erstellst du zunächst die Instanz http von HTTPClient und verbindest dich mit der API:

HTTPClient http;
client.connect(host, httpsPort);
http.begin(client, host);

Danach prüfst du, ob die Verbindung steht und speicherst die JSON-Daten, die du von der API erhältst, in der Variablen payload. Anschließend dekodierst du sie und “ziehst” dir die oben genannte Zeitangabe als Unixtime. Diese Zeitangabe speicherst du in der Variablen launchTime.

if (http.GET() == HTTP_CODE_OK) {
  String payload = http.getString();
  DynamicJsonDocument doc(3072);
  deserializeJson(doc, payload);

  launchTime = doc["date_unix"]; //Startzeit speichern

Als nächstes berechnest du den Countdown. Hierfür benötigst du natürlich auch die aktuelle Uhrzeit. Diese hast du weiter oben im Sketch schon ermittelt und in der Variablen currentTime gespeichert (Schaue hierfür in den vollständigen Sketch).

countdown = launchTime - currentTime;

Den SpaceX Countdown anzeigen

Jetzt lässt du deinen Countdown laufen, bis die Startzeit erreicht wurde:

while (countdown != 0) {

Du zeigst zunächst die Sekunden, die es noch bis zum Start dauert, auf dem Display an. Hier kommt eine Funktion ins Spiel, die du im vollständigen Sketch findest:

drawDigits(countdown);

Danach ziehst du vom Wert in der Variablen countdown eine Sekunde ab und wartest mit deinem delay auch genau diese eine Sekunde, bevor du die nächste Zahl anzeigst.

countdown -= 1;
delay(1000);

Wenn der While-Loop ausgelaufen ist, die Variable countdown also eine Null enthält, löschst du das Display, startest dafür den LED-Ring und lässt ihn z.B. eine halbe Stunde angeschaltet.

lc.clearDisplay(0);

for (int i = 0; i < 12; i++) {
  pixels.setPixelColor(i, pixels.Color(255, 0, 0));
  pixels.show();
  delay(1);
}

delay(1800000); //30 Minuten

In diesem Beispiel leuchten alles LEDs auf dem Ring in einem satten Rot auf. Du kannst dir aber natürlich auch etwas raffinierteres überlegen!

Spacex Countdown ESP8266
Ein möglicher Aufbau für das Projekt

Und das war es auch schon! Zuletzt noch ein Hinweis: Wenn dein Delay am Ende des Sketchs ausgelaufen ist, folgt die nächste Abfrage der SpaceX API. Möglicherweise liegen dann aber noch keine neuen Daten vor. Bis das soweit ist, kann es mitunter einige Stunden oder auch Tage dauern – du könntest deinen ESP8266 also auch erst einmal gar keine neue Abfrage stellen lassen.

Sketch als .txt anschauen

#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecure.h>

WiFiClientSecure client;

//NTP
#include <NTPClient.h>
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

//SpaceX API URL und Port
const char* host = "https://api.spacexdata.com/v4/launches/next";
const int httpsPort = 443;

//WLAN Zugangsdaten
const char* ssid = "NETWORK";
const char* password =  "PASSWORD";

//Variablen
long launchTime;
long currentTime;
long countdown;

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

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

int leds = 12; //Anzahl der LEDs
int ledPin = 14; //14=D5, Pin, an dem der NeoPixel angeschlossen ist

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(leds, ledPin, NEO_GRB + NEO_KHZ800);

void getCurrentTime() {

  timeClient.update();
  currentTime = timeClient.getEpochTime();
  
  Serial.print("Current Time= ");
  Serial.println(currentTime);
}

void drawDigits(int num) {
  for (int i = 0; i < NUM_DIGITS ; i++) {
    lc.setDigit(0, i, num % 10, false);
    num /= 10;
    if (num == 0)
      return;
  }
}

void setup() {
  Serial.begin(115200);
  client.setInsecure();

  //Einstellungen des 7-Segment-Displays
  lc.shutdown(0, false);
  lc.setIntensity(0, 8);
  lc.clearDisplay(0);

  //Einstellungen des NeoPixels
  pinMode (ledPin, OUTPUT);
  pixels.begin();
  pixels.setBrightness(255); //Helligkeit: 0 (aus) - 255

  for (int i = 0; i < 12; i++) {
    pixels.setPixelColor(i, pixels.Color(0, 0, 0));
    pixels.show();
    delay(1);
  }

  WiFi.begin(ssid, password); //Mit dem WLAN verbinden

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  delay(1000);
  Serial.println("Hello, world!");
}

void loop() {
  if ((WiFi.status() == WL_CONNECTED)) {

    getCurrentTime();

    HTTPClient http;
    client.connect(host, httpsPort);
    http.begin(client, host);

    if (http.GET() == HTTP_CODE_OK) {
      String payload = http.getString();
      DynamicJsonDocument doc(3072);
      deserializeJson(doc, payload);

      launchTime = doc["date_unix"]; //Startzeit speichern
      Serial.print("Launch Time: ");
      Serial.println(launchTime);

      countdown = launchTime - currentTime; //Countdown = Startzeit - aktuelle Zeit
    } else {
      Serial.print("Error on HTTP request (SpaceX): ");
      Serial.println(http.GET()); //Error Code
    }
  }

  while (countdown != 0) { //Solange die Startzeit nicht erreicht wurde
    Serial.print("Time Until Launch: ");
    Serial.println(countdown);
    drawDigits(countdown);
    countdown -= 1;
    delay(1000);
  }

  Serial.println("LAUNCH!"); // Startzeit = aktuelle Zeit
  lc.clearDisplay(0);

  for (int i = 0; i < 12; i++) {
    pixels.setPixelColor(i, pixels.Color(255, 255, 0));
    pixels.show();
    delay(1);
  }

  delay(1800000);
}
]]>
Ein Babyphone mit künstlicher Intelligenz und einem Arduino Nano 33 BLE Sense https://polluxlabs.net/arduino-projekte/ein-babyphone-mit-kuenstlicher-intelligenz-und-einem-arduino-nano-33-ble-sense/ Mon, 18 Jan 2021 22:36:23 +0000 https://polluxlabs.net/?p=4884 Ein Babyphone mit künstlicher Intelligenz und einem Arduino Nano 33 BLE Sense Weiterlesen »

]]>

Ein Babyphone ist eine praktische Sache, sollte das eigene Kleinkind mal außer Hörweite sein. Im Prinzip ist solch ein Gerät nichts anderes als ein Sender mit Mikrofon, der per Funk Geräusche an einen Empfänger überträgt. Weint oder schreit ein Baby aus dem Lautsprecher, erkennt jeder Mensch sofort, was hier los ist.

Aber kann eine künstliche Intelligenz auch lernen, Weinen und Schreien von anderen Geräuschen zu unterscheiden? Klar! Hier stellen wir ein Projekt vor, das mithilfe von Machine Learning und TinyML genau das macht. Alles was du dafür brauchst, ist ein Arduino Nano 33 BLE Sense und einen kostenlosen Account bei Edge Impulse.

Fortgeschrittene

3 – 4 Stunden

ca. 40€

Für dieses Projekt benötigst du:

Arduino® Board Nano 33 BLE Sense with headers
Arduino® Board Nano 33 BLE Sense with headers
Arduino Board Nano 33 BLE Sense with headers; Inhalt: 1 Stück

Das vorgestellte Projekt stammt vom Maker Ish Ot Jr. und wurde im Arduino Project Hub veröffentlicht. Du findest die Beschreibung (Englisch) hier. Im Folgenden beschäftigen wir uns mit den Voraussetzungen für das Babyphone mit KI, gehen auf wichtige Aspekte genauer ein und geben einen Ausblick auf weitere Anwendungen und Möglichkeiten.

Grundlagen

Das Babyphone mit dem Arduino Nano 33 BLE Sense funktioniert im Kern so: Zunächst sammelst du Geräusche von (d)einem Baby, die die Künstliche Intelligenz später erkennen soll. Damit sie diese von allen möglichen anderen Geräuschen unterscheiden kann, benötigst du auch Nebengeräusche, die im Kinderzimmer auftreten können – und zwar so viele Samples wie möglich, auch von der Stille eines träumenden Babys. 😉

Die Audiodaten kannst du direkt mit deinem Arduino sammeln. Wenn du noch nicht weißt, wie das geht, lese zunächst diese beiden Tutorials:

Wie du siehst, steht der Service von Edge Impulse im Mittelpunkt. Diesen kannst du kostenlos nutzen, um Daten zu sammeln, zu KI-Modellen zu verarbeiten und auf einem Microcontroller zu deployen.

Audio sammeln und ein Modell entwickeln

Wenn du nun weißt, wie du grundsätzlich Daten mit deinem Arduino Nano sammelst und in Edge Impulse speicherst, kannst du damit loslegen. Im Projekt im Arduino Project Hub legt der Autor zwei Labels an: Weinen (crying) und Geräusche (noise).

Wenn du für jedes deiner Labels mehrere Minuten Audio gesammelt hast, kann es weitergehen. In seinem Tutorial im Arduino Project Hub erläutert der Autor, welche Einstellungen er für sein Impulse Design verwendet. Damit erhält er eine Genauigkeit seines Modells von gut 86%.

Investiere in diesen Schritt des Projekts ruhig etwas Zeit und spiele mit den Einstellungen herum. Es ist wichtig, dass die einzelnen Labels (bzw. Features) gut von einander unterscheidbar sind, wie hier zu sehen. Diese Daten stammen aus einem anderem KI-Projekt, bei dem es um das Erkennen von Gesten geht.

KI-Modell in Edge Impulse
Beispiel für gut getrennte Features

Wenn du auf keinen grünen Zweig kommst, könnte eine Möglichkeit sein, einfach noch mehr Daten zu sammeln. Je mehr Material deine KI zum Lernen hat, desto besser. Achte auch darauf, dass du in Edge Impulse auch genügend Testdaten hast – diese dürfen sich nicht mit den Trainingsdaten überschneiden, um zuverlässige Ergebnisse zu erhalten.

So bringst du die KI auf deinen Arduino

Wenn du zufrieden bist mit deinem KI-Modell, wähle unter dem Menüpunkt Deployment die Kachel Arduino Library und lade dir die ZIP-Datei herunter. Hier enthalten sind eine Bibliothek und ein Sketch, den du im Folgenden etwas modifizierst.

Du könntest unter Deployment auch eine Firmware für deinen Arduino Nano 33 BLE Sense erstellen lassen. Dann könntest du die Auswertungen der KI jedoch nur im Seriellen Monitor verfolgen – etwas unpraktisch für ein Babyphone. Deshalb kommt der Sketch ins Spiel.

Im Tutorial im Project Hub geht der Autor sehr genau auf seine Anpassungen ein und erklärt, wie er die direkt am Arduino verbaute RGB LED verwendet, um darzustellen, ob ein Baby gerade weint oder nicht. Grün steht hier für “Alles in Ordnung”, also dafür, dass die Künstliche Intelligenz nur Noise erkennt. Rot hingegen für Weinen.

Wie geht es weiter?

Das vorgestellte Projekt zeigt sehr anschaulich, wie man mit einem einigermaßen günstigen Microcontroller und Edge Impulse relativ einfach ein KI-Projekt umsetzen kann. Du könntest das Modell noch verfeinern und nicht alles was nicht Weinen ist, einfach unter Noise zusammenfassen. So könnten weitere Labels die Ergebnisse noch verbessern.

Für diese detailliertere Auswertung ist die interne RGB LED vermutlich nicht ausreichend. Auf Pollux Labs findest du viele Tutorials zu Displays und auch zu einem NeoPixel LED Ring.

Wie sieht es mit anderen Geräuschen aus? Duscht gerade jemand? Oder schleudert deine Waschmaschine unruhig und könnte bald kaputtgehen? Das könnten alles Anwendungsfälle für dich sein. 🙂

]]>
Erhalte eine Nachricht aufs Smartphone, wenn die Post da ist https://polluxlabs.net/esp8266-projekte/erhalte-eine-nachricht-aufs-smartphone-wenn-die-post-da-ist/ Wed, 06 Jan 2021 12:20:49 +0000 https://polluxlabs.net/?p=4657 Erhalte eine Nachricht aufs Smartphone, wenn die Post da ist Weiterlesen »

]]>
Gehst du ab und an vergeblich zum Briefkasten? Das ist jetzt vorbei! Dieses Projekt sendet dir eine Telegram-Nachricht aufs Smartphone, sobald der Briefträger die Post gebracht hat.

Hierfür verwenden wir einen Wemos D1 Mini und einen PIR-Bewegungsmelder. Du kannst aber natürlich auch einen anderen ESP8266 verwenden. Ebenso kannst du zum Beispiel ein Radarmodul verwenden, um die Bewegung im Briefkasten festzustellen.

Da wir im Briefkasten keine Steckdose haben, verwenden wir eine Powerbank für die Stromversorgung und versetzen den Microcontroller die meiste Zeit in Tiefschlaf. Eine Bedingung gibt es allerdings: Der D1 Mini bzw. ESP8266 muss sich mit deinem WLAN verbinden können – dieses sollte also bis zu deinem Briefkasten reichen.

Fortgeschrittene

1 – 2 Stunden

ca. 15€

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

Angebot
AZDelivery ESP32 D1 Mini NodeMCU WiFi Modul + Bluetooth Internet Entwicklungsboard kompatibel mit Arduino inklusive E-Book!
AZDelivery ESP32 D1 Mini NodeMCU WiFi Modul + Bluetooth Internet Entwicklungsboard kompatibel mit Arduino inklusive E-Book!
✅ Unser D1 ESP32 ist kompatibel mit der Arduino-IDE, Lua und MicroPython und NodeMCU.
10,99 €
Haobase 100x 1N4007 DO-41 Gleichrichter Diode 1A 1000V
Haobase 100x 1N4007 DO-41 Gleichrichter Diode 1A 1000V
Sperrspannung: 1000 V.; Nennstrom: 1A.; Diodengröße: Ca. 4,5 mm (L) x 2,5 mm (D)...
4,90 €
Widerstand 1 K Ohm, 20 Stück, Metallschicht 0.6W 1% Metallfilm Widerstände
Widerstand 1 K Ohm, 20 Stück, Metallschicht 0.6W 1% Metallfilm Widerstände
Bauform Axial 207; Toleranz +/- 1%; Abmessungen D: 2,5mm L: 6,8mm; 0,6W Belastbarkeit bei...
AZDelivery Breadboard Kit - 3X Jumper Wire m2m/f2m/f2f + 3er Set MB102 Breadboard kompatibel mit Raspberry Pi inklusive E-Book!
AZDelivery Breadboard Kit - 3X Jumper Wire m2m/f2m/f2f + 3er Set MB102 Breadboard kompatibel mit Raspberry Pi inklusive E-Book!
✅ Steckbrett für schnellen Aufbau elektronischer Schaltungen mit 830 Kontakten.; ✅...
9,99 €

Alternativ kannst du auch einen NodeMCU ESP8266 und/oder ein Radarmodul verwenden:

Vorbereitungen

Ein paar Dinge musst du eventuell vorab erledigen – wie, erfährst du in diesen Tutorials: Richte dir zunächst einen Telegram-Bot ein, um Nachrichten vom D1 Mini auf deinem Smartphone erhalten zu können. Außerdem musst du deinen D1 Mini bzw. ESP8266 in deiner Arduino IDE verfügbar machen, um ihn von dort aus programmieren zu können.

Das Projekt aufbauen

Montiere zunächst alle Teile wie folgt auf deinem Breadboard.

Aufbau auf dem Breadboard
Aufbau auf dem Breadboard

Schauen wir zunächst auf die Verbindung der Pins D0 und RST: Hiermit kannst du deinen Microcontroller aufwecken, wenn von D0 ein Signal an RST gesendet wird. In diesem Projekt soll dieses Signal jedoch vom Bewegungssensor kommen. Damit es also nicht fälschlicherweise doch vom Pin D0 gesendet wird, verwenden wir eine Diode, die Strom von diesem Pin blockiert (achte beim Einbau auf die Richtung des weißen Rings). Falls du keine Diode zur Hand hast, sollte das Projekt behelfsweise auch mit einer einfachen Drahtbrücke funktionieren.

Noch ein Hinweis: Solange die beiden Pins miteinander verbunden sind, kannst du keinen Sketch hochladen. Hierfür musst du die Verbindung zunächst trennen.

Das Wecksignal für den D1 Mini

Du weckst deinen Microcontroller also über ein HIGH am Reset-Pin auf. Hierfür benötigst du allerdings einen eindeutigen Wechsel von LOW auf HIGH. Wenn du deinen PIR-Sensor direkt an den Reset-Pin anschließt, erhältst du kein korrektes Signal, was zu mehreren Resets hintereinander führen kann – oder einfach nicht funktioniert, wie in unseren Tests.

___STEADY_PAYWALL___

In Anlehnung an dieses Tutorial verbauen wir einen NPN-Transistor (BC547), der uns dabei hilft, ein eindeutiges Signal vom PIR-Sensor an den Reset-Pin des D1 Mini zu übertragen. Sollte das bei dir trotzdem zu ungewollten Neustarts (also Aufwachen) führen, wird im verlinkten Tutorial auch noch ein Aufbau mit einem Kondensator beschrieben, der dir weiterhelfen könnte.

Mehr über NPN-Transistoren erfährst du in diesem Artikel. In diesem Tutorial erfährst du mehr über den Bewegungssensor HC-SR501.

Der Sketch

Kommen wir also zum Code. Zunächst benötigst du ein paar Bibliotheken, die du zu Beginn des Sketchs einbindest. Falls noch nicht geschehen, installiere im Bibliotheksmanager die jeweils neueste Version.

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>

Anschließend folgen einige Definitionen und Konstanten für deine Zugangsdaten zum WLAN und zu deinem Telegram Bot. Zur Erinnerung: Wie du die benötigten Daten für Telegram erhältst, erfährst du im oben genannten Tutorial.

//Deine WLAN-Zugangsdaten
const char* ssid = "NETWORK";
const char* password = "PASSWORD";

//Dein Bot-Token
#define botToken "BOT-TOKEN"  // den Bot-Token bekommst du vom Botfather)

//Deine User ID
#define userID "USER-ID"

Im letzten Schritt vor der Setup-Funktion erstellst du eine Instanz von WiFiClientSecure und initialisierst mit dieser die Verbindung zum Telegram-Bot.

WiFiClientSecure client;
UniversalTelegramBot bot(botToken, client);

Die Setup-Funktion

Im Sketch für dieses Projekt benötigst du nur die Setup-Funktion, die nach dem Start des Wemos D1 Mini abläuft. Hier startest du die Internet-Verbindung und sendest die Nachricht, dass die Post da ist, an dein Smartphone (also an deinen Telegram-Bot). Zuletzt legst du deinen Microcontroller wieder schlafen.

Sobald er ein neues Signal von deinem Bewegungsmelder erhält, wacht er wieder auf und durchläuft das Setup erneut. Wie du siehst, benötigst du also keinen Loop.

Zuerst konfigurierst du den client, den du oben erzeugt hast, startest den Seriellen Monitor und gibst dort die Nachricht aus, dass dein D1 Mini aufgeweckt wurde:

client.setInsecure();
Serial.begin(115200);
Serial.println("Awake");

Anschließend stellst du die Verbindung zu deinem WLAN-Netzwerk her:

Serial.print("Connecting to WiFi: ");
Serial.println(ssid);

WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
  Serial.print(".");
  delay(500);
}

Serial.println("");
Serial.println("Connected!");

Zuletzt sendest du eine Nachricht an deinen Telegram-Bot mit der Funktion bot.sendMessage() – wartest 2 Sekunden und legst den D1 Mini wieder schlafen:

bot.sendMessage(userID, "Your mail has arrived!", "");

delay(2000);
Serial.println("Going to sleep");
ESP.deepSleep(0);

Für den Tiefschlaf verwendest du die Funktion ESP.deepSleep() mit dem Parameter 0. Diese Null sorgt dafür, dass dein Microcontroller ohne zeitliches Limit in den Schlaf versetzt wird – das heißt er wird erst wieder durch ein Signal vom Bewegungssensor aufgeweckt.

Und das war es auch schon – eigentlich ganz einfach. Hier nun der gesamte Sketch zum Rauskopieren.

//Libraries
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>

//WiFi Credentials
const char* ssid = "NETWORK";
const char* password = "PASSWORD";

//Telegram Bot Credentials
#define botToken "BOT-TOKEN"
#define userID "USER-ID"

WiFiClientSecure client;
UniversalTelegramBot bot(botToken, client);

void setup() {
  client.setInsecure();
  Serial.begin(115200);
  Serial.println("Awake");

  //Connecting to WiFi
  Serial.print("Connecting to WiFi: ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }

  Serial.println("");
  Serial.println("Connected!");

  bot.sendMessage(userID, "Your mail has arrived!", "");

  delay(2000);
  Serial.println("Going to sleep");
  ESP.deepSleep(0);
}

void loop() {
}

Wie geht es weiter?

Du hast nun einen Post-Melder mit dem Bewegungssensor HC-SR501. Je nachdem, wie dein Briefkasten beschaffen ist, kann ein Radar-Modul die bessere Wahl sein.

Vielleicht fallen dir aber auch noch ganz andere Lösungen ein, um zu erkennen, dass dein Briefkasten geöffnet wurde. Nerdiger und ambitionierter wäre zum Beispiel eine Variante, die KI nutzt. Auf Pollux Labs kannst du die Grundlagen von Künstlicher Intelligenz auf Microcontrollern lernen.

]]>
Arduino Wettervorhersage https://polluxlabs.net/arduino-projekte/arduino-wettervorhersage/ Wed, 02 Dec 2020 10:34:52 +0000 https://polluxlabs.net/?p=4363 Arduino Wettervorhersage Weiterlesen »

]]>
Eine Wetterstation mit aktuellen Werten ist eine Sache – aber zu wissen, wie das Wetter in einigen Stunden sein wird, eine ganz andere. In diesem Projekt baust du dir so eine einfache Wettervorhersage mit dem Luftdrucksensor BMP180 und einem Servo.

Zugegeben, mit den Meteorologen in den Nachrichten kann dieses Projekt nicht mithalten, aber möglicherweise kann es dich vor den ganz großen Wetterumschwüngen warnen.

Anfänger

1 – 2 Stunden

ca. 10 € + Arduino

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

So funktioniert die Wettervorhersage

Wie eingangs erwähnt, ist eine Wettervorhersage eine komplexe Angelegenheit, an der Spezialisten mit Großcomputern arbeiten. Es gibt jedoch einen Zusammenhang, den wir uns in diesem Projekt zunutze machen: Steigt der Luftdruck, bessert sich das Wetter – fällt er, wird das Wetter schlecht.

Auch das ist eine starke Vereinfachung der Realität sein, aber dieser Zusammenhang soll uns hier einmal genügen. Immerhin wird dieses Prinzip schon seit Jahrhunderten angewandt. Sicherlich kennst du alte Barometer wie dieses hier:

Dosen-Barometer, Quelle: Wikipedia/Langspeed

Mehr über Barometer und die möglichen Anwendungen erfährst du auf Wikipedia. In unserer Arduino Wettervorhersage messen wir in regelmäßigen Abständen den Luftdruck und vergleichen ihn mit dem zuletzt gemessenen Wert. Fällt der Luftdruck immer weiter, dreht ein Servo-Motor einen Zeiger nach links in Richtung schlechtes Wetter. Wenn der Luftdruck kontinuierlich steigt, bewegt sich der Zeig nach rechts – es wird also gutes Wetter geben. Übrigens: Mit unserer ESP8266 Wetterstation speicherst du Daten zum Luftdruck lokal in einer Datenbank.

Luftdruck messen mit dem BMP180

Für die Messung des Luftdrucks verwenden wir den Sensor BMP180. Du kannst auch den genaueren BMP280* oder einen BME280* verwenden. Letzterer misst auch die Luftfeuchte, die wir in diesem Projekt jedoch nicht brauchen.

Diese Sensoren lassen sich leicht per I²C anschließen und mit einer passenden Bibliothek komfortabel verwenden. In diesem Tutorial erfährst du mehr darüber, wie du den BMP180 anschließt und verwendest.

Das Wetter mit einem Servo anzeigen

Es gibt viele denkbare Möglichkeiten, um die Veränderungen des Luftdrucks anzuzeigen: Verschiedene Displays oder auch ein NeoPixel LED-Ring. Hier verwenden wir jedoch einen Servo-Motor und eine passende Schablone. Diese können z.B. Kinder farbig gestalten und sie hat ebenso den gewissen Retro-Charme. 🙂

Schablone für den Servo der Wettervorhersage
Schablone für den Servo

Was ein Servo-Motor ist, wie funktioniert und was es alles zu beachten gibt, erfährst in diesem Tutorial zu Servos.

Der Aufbau der Arduino Wettervorhersage

Es dauert nur wenige Minuten, die Wettervorhersage auf deinem Breadboard aufzubauen. Orientiere dich hierbei an diesem Schema:

Aufbau der Arduino Wettervorhersage

Achte darauf, dass du den Servo mit 5V und den BMP180 mit 3,3V versorgst. Die Beschriftung der Pins des BMP180 befindet sich auf seiner Unterseite – vergewissere dich, dass du sie richtig am Arduino angeschlossen hast, bevor du ihn mit Strom versorgst.

Der Sketch

Es sind nicht viele Zeilen Code nötig, um die Wettervorhersage zum Laufen zu bringen. Starte wie so oft, indem du die nötigen Bibliotheken einbindest. Wie du sie installierst, erfährst du in den oben genannten Tutorials.

#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <Servo.h>

Anschließend erstellst du zwei Objekte – eines für den Luftdruck-Sensor und eines für den Servo:

Adafruit_BMP085 bmp;
Servo myServo;

Dazu benötigst du noch ein paar Variablen, um die Werte des Sensors und die gewünschte Position des Servos zu speichern:

int servoPosition;
long currentPressure;
long oldPressure;
int delta;

Die Setup-Funktion

Hier startest du den Seriellen Monitor und vergewisserst dich, dass der BMP180 richtig angeschlossen und funktionstüchtig ist. Anschließend weist du dem Servo den Anschlusspin 8 zu und drehst den Zeiger nach oben – auf 90°.

void setup() {
  Serial.begin(115200);
  if (!bmp.begin()) {
    Serial.println("Sensor not found!");
    while (1) {}
  }

  myServo.attach(8);
  myServo.write(90);
}

Übrigens: Wenn du nicht weißt, in welcher Position sich dein Servo gerade befindet und in welche Position der Zeiger bei der Montage schauen soll, dann bringe ihn erst nach dem Start an. Zu Beginn des Sketchs steht der Servo auf 90° – sodass der Zeiger nach oben gerichtet ist.

Der Loop der Arduino Wettervorhersage

Hier misst du als erstes den aktuellen Luftdruck und gibst ihn im Seriellen Monitor aus:

  currentPressure = bmp.readPressure();
  Serial.print("Current Pressure = ");
  Serial.print(currentPressure);
  Serial.println(" Pa");

Wie du siehst, geht das ganz einfach mit der Funktion bmp.readPressure(). Gleichzeitig speicherst du diesen Wert – der in Pascal ausgegeben wird – in der Variablen currentPressure.

Danach fragst du ab, ob sich der aktuelle Luftdruck gegenüber der Messung davor (gespeichert in der Variablen oldPressure) verändert hat. Falls ja, speicherst du diese Veränderung in der Variablen delta.

 if (oldPressure && currentPressure != oldPressure) {
   delta = currentPressure - oldPressure;

Im If-Statement siehst du die Bedingung oldPressure && – diese befindet sich hier, da es im ersten Messdurchgang noch keine alte Messung gibt. Erst wenn in dieser Variablen eine Zahl hinterlegt ist – sie also nicht mehr auf 0 bzw. false steht – wird diese Bedingung wahr. Alternativ kannst du auch folgendes schreiben:

 if (oldPressure == true && currentPressure != oldPressure) {

Den Servo steuern

Kommen wir zum Servo und der Anzeige der Wetterlage. Um die Veränderung des Luftdrucks in der Variablen delta anzuzeigen, musst du dem Servo mitteilen, wo er sich hindrehen soll. Ein Servo kann eine Position zwischen 0° und 180° einnehmen – zu Beginn des Sketchs steht er in der Mitte, also auf 90°.

Wir nehmen mal an, dass die maximale Veränderung in unserem Messzeitraum (mehr dazu gleich) bei +- 100 Pa liegt. Bei -100 Pa soll der Servo auf 0° fahren, bei +100 Pa entsprechend auf 180°. Hinweis: Möglicherweise liegen wir mit dieser Einschätzung falsch – hier sind also deine eigenen Experimente gefragt. Verfolge im Seriellen Monitor die Messwerte und kalibriere deine Wetterstation entsprechend.

Jedenfalls musst du die beiden Wertebereiche +-100 Pa und 0-180° unter einen Hut bringen. Hierfür bietet sich die Funktion map() an:

servoPosition = map(delta, -100, 100, 0, 180);

Hier nimmst du die aktuelle Veränderung delta, ihren möglichen Wertebereich +-100 und “mappst” diesen Wert auf die möglichen Winkel des Servos: 0° bis 180°. Heraus kommt der Winkel, der der Veränderung des Luftdrucks entspricht. Diesen speicherst du in der Variablen servoPosition.

Anschließend steuerst du deinen Servo auf diese Position:

myServo.write(servoPosition);

Danach machst du die aktuelle Messung zur alten Messung oldPressure, mit der du die nächste vergleichst. Und als letztes wartest du eine gewisse Zeit bis zur Messung, in diesem Fall 5 Minuten bzw. 300.000 Millisekunden.

oldPressure = currentPressure;
delay(300000);

Hier nun der gesamte Sketch der Arduino Wettervorhersage zum Rauskopieren und Hochladen. Viel Spaß! 🙂

#include <Wire.h>
#include <Adafruit_BMP085.h>

#include <Servo.h>

Adafruit_BMP085 bmp;
Servo myServo;

int servoPosition;
long currentPressure;
long oldPressure;
int delta;

void setup() {
  Serial.begin(115200);
  if (!bmp.begin()) {
    Serial.println("Sensor not found!");
    while (1) {}
  }

  myServo.attach(8);
  myServo.write(90);
}

void loop() {
  currentPressure = bmp.readPressure();
  Serial.print("Current Pressure = ");
  Serial.print(currentPressure);
  Serial.println(" Pa");

  if (oldPressure && currentPressure != oldPressure) {
    delta = currentPressure - oldPressure;
    Serial.print("Change: ");
    Serial.print(delta);
    Serial.println();
  }

  servoPosition = map(delta, -100, 100, 0, 180);
  myServo.write(servoPosition);
  
  oldPressure = currentPressure;

  delay(300000);
}

Fehlen dir noch Bauteile? Dann wirf einen Blick in unsere Übersicht der besten Arduino Starter Kits.

]]>
Gute oder schlechte Nachrichten? Mit einer Sentimentanalyse findest du es heraus. https://polluxlabs.net/esp8266-projekte/pruefe-die-nachrichten-mit-einer-sentimentanalyse/ Mon, 23 Nov 2020 17:12:21 +0000 https://polluxlabs.net/?p=4243 Gute oder schlechte Nachrichten? Mit einer Sentimentanalyse findest du es heraus. Weiterlesen »

]]>
In diesem Projekt lädst du dir mit einem ESP32 aktuellen Schlagzeilen aus dem Internet und findest mit einer Sentimentanalyse heraus, ob sie positiv oder negativ sind. Ein NeoPixel LED-Ring zeigt dir dann an, ob die Nachrichtenlage gerade gut (grün), schlecht (rot) oder etwas dazwischen ist.

Fortgeschrittene

1 – 2 Stunden

ca. 12 €

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

Die Basics

Vieles, was in diesem Projekt zum Einsatz kommt, haben wir bereits in Tutorials beschreiben. Wenn du etwas davon noch nicht kennst, wirf zunächst dort einen Blick hinein:

Im Folgenden konzentrieren wir uns darauf, mit einer Sentimentsanalyse herauszufinden, ob eine Nachricht gut oder schlecht ist – und das dann mit dem NeoPixel anzuzeigen.

Was ist eine Sentimentanalyse?

Für einen Menschen ist es normalerweise kein Problem zu beurteilen, ob die Stimmung (Sentiment) einer Aussage positiv, negativ oder irgendetwas dazwischen ist. Für einen Computer ist das etwas schwieriger, aber trotzdem möglich – mit den richtigen Algorithmen und Wörterbüchern.

Hierfür liest der Computer einen Text ein und beurteilt die Wörter und Wortzusammenhänge, die er dort vorfindet. Im Hintergrund steckt immer ein Wörterbuch, in dem Begriffe von Menschen bewertet wurden: Pech und Trauer sind negative Begriffe, Glück und Positiv hingegen – positive.

Aber so einfach ist es leider nicht: Oft steckt die Bedeutung einer Aussage nicht in einem einzelnen Wort, sondern in einer Wendung, die aus mehreren Wörtern besteht. Positiv mag zwar in den meisten Fällen etwas Gutes sein, wenn jedoch jemand auf ein Virus positiv getestet wurde, ist genau das Gegenteil der Fall.

Mehr über dieses spannende Thema findest du auf Wikipedia.

Der Aufbau des Projekts

Du musst im Prinzip nur deinen NeoPixel mit deinem ESP32 wie folgt verbinden:

NeoPixelESP32
VCC3v3
GNDGND
DI14

Natürlich kannst du hierfür einfach ein Breadboard verwenden. Etwas mehr Stimmung verleihst du mit einer Kugel und einem Sockel aus dem 3D-Drucker. Passende Plastikkugeln* findest du für wenig Geld im Bastelladen oder auf Amazon.

Die Schlagzeilen laden

Für dieses Projekt lädst du dir zunächst die aktuellen Schlagzeilen von einer kostenlosen API. Hierfür bietet sich der Service newsapi.org an. Erstelle dir dort zunächst einen kostenlosen Account, um deinen API Key zu erhalten, den du für die Abfrage der Nachrichten benötigst.

Danach kannst du z.B. länderspezifische Top Headlines abfragen, die dir als JSON zur Verfügung gestellt werden. Du kannst die Nachrichten jedoch auch nach Ländern, Suchbegriffen und Quellen filtern. Da die Sentimentanalyse, die wir später verwenden, kein Deutsch versteht, verwenden wir hier die Top Headlines der USA.

___STEADY_PAYWALL___

Die URL für den API Call lautet dann wie folgt – wobei du <<API-KEY>> durch deinen eigenen API Key ersetzen musst.

http://newsapi.org/v2/top-headlines?country=us&apiKey=<<API-KEY>>

Mit dieser Abfrage erhält dein ESP32 Daten im JSON-Format, die er – wie im oben genannten Tutorial beschrieben – mit der Bibliothek ArduinoJson verarbeiten und der Sentimentanalyse weitergeben kann.

Übrigens: Wenn du die aktuellen Nachrichten zum Corona-Virus analysieren möchtest, verwende die folgende URL (Covid ist in englischen Medien gängiger als Corona).

http://newsapi.org/v2/everything?q=covid&apiKey=<<API-KEY>>

Die Stimmung der Schlagzeilen bestimmen

Hier kommt eine weitere kostenlose API ins Spiel: meaningcloud.com – auch hier benötigst du einen kostenlosen Developer Account, der dir eine begrenzte (aber hier ausreichende) Anzahl von Abfragen zur Verfügung stellt. Für diesen Service benötigst du ebenfalls einen API Key, den du in der Abfrage-URL hinterlegen musst.

Diese URL sieht folgendermaßen aus:

http://api.meaningcloud.com/sentiment-2.1?key=<<API-KEY>>&of=json&txt=<<TEXT>>&lang=en

Wie du siehst, übergibst du die Schlagzeilen von newsapi.org in der URL an die API. Diese steckt in der Variable article, allerdings mit Leerzeichen zwischen den Wörtern, was in einer URL natürlich nicht funktioniert. Deshalb ersetzt du diese Leerzeichen wie folgt durch ein %20:

article.replace(" ", "%20");

Nun kannst du die URL für die Sentimentanalyse flexibel zusammenbauen:

http://api.meaningcloud.com/sentiment-2.1?key=<<API-KEY>>&of=json&txt=" + article + "&lang=en

Im Sketch passiert das in einem For-Loop insgesamt 15 Mal – für die 15 neuesten Schlagzeilen.

Die Ergebnisse der Sentimentanalyse

Jetzt wird es spannend: Die API spielt eine ganze Menge Daten im JSON-Format zurück, in denen uns hier aber nur die Stimmung der Schlagzeile interessiert. Hierfür gibt es fünf Werte:

Sehr positivP+
PositivP
NeutralNEU
NegativN
Sehr negativN+

Darüberhinaus gibt es noch den Wert NONE – falls keine Stimmung ermittelt werden konnte. Jedem dieser Werte ordnest du im Sketch eine Zahl zu: von 2 für P+ bis zu -2 für N+. Hier das Beispiel für eine sehr positive Schlagzeile:

if (score_tag == "P+") {
  sentiment += 2;
}

Diese Zahlen addierst du miteinander und erhältst dadurch einen Wert – den aktuellen Zustand der Welt. 😉

Den Gesamtwert als Farbe ausgeben

Theoretisch könnte sich der Gesamtwert der 15 Schlagzeilen zwischen -30 und +30 bewegen. In den meisten Fällen reicht jedoch eine Skala von -5 bis +5.

Diesen 11 Werten innerhalb der Skala ordnest du Farben zu, die du als RGB-Werte anlegst – und lässt deinen NeoPixel LED-Ring dann in der Farbe des Gesamtwerts leuchten. Wir haben uns für ein Farbspektrum von Rot zu Grün entschieden – mit Gelb in der Mitte für den neutralen Wert Null:

int minus5[] = {255, 0, 0};
int minus4[] = {255, 51, 0};
int minus3[] = {255, 102, 0};
int minus2[] = {255, 153, 0};
int minus1[] = {255, 204, 0};
int neutral[] = {255, 255, 0};
int plus1[] = {204, 255, 0};
int plus2[] = {153, 255, 0};
int plus3[] = {102, 255, 0};
int plus4[] = {51, 255, 0};
int plus5[] = {0, 255, 0};

Wenn du selbst die Farben bestimmten möchtest, eignet sich hierfür der Color Picker von Google.

Im Sketch fehlt nun nur noch etwas Code, um entsprechend des Gesamtwerts der Sentimentanalyse eine Farbe auszugeben. Für den Wert -4 zum Beispiel:

switch (sentiment) {
  case -4:
    for (int i = 0; i < 12; i++) {
      pixels.setPixelColor(i, minus4[0], minus4[1], minus4[2]);
      pixels.show();
    }
    break;

Alle Werte von -5 und darunter (sowie +5 und darüber) fängst du davor schon mit einer If-Abfrage ab:

if (sentiment <= -5) {
  for (int i = 0; i < 12; i++) {
    pixels.setPixelColor(i, minus5[0], minus5[1], minus5[2]);
    pixels.show();
  }

Ganz am Ende des Sketchs fügst du noch einen Delay ein, der bestimmt, wann die nächste Abfrage der Schlagzeilen und die Sentimentanalyse erfolgen soll – z.B. alle 30 Minuten:

delay(1800000);

Hier nun der gesamte Sketch:

Sketch als .txt anschauen

//Libraries
#include <ArduinoJson.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <Adafruit_NeoPixel.h>

//Pin for the NeoPixel
int ledPin = 14;

//Initiate NeoPixel
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(12, ledPin, NEO_GRB + NEO_KHZ800);

//Colors
int minus5[] = {255, 0, 0};
int minus4[] = {255, 51, 0};
int minus3[] = {255, 102, 0};
int minus2[] = {255, 153, 0};
int minus1[] = {255, 204, 0};
int neutral[] = {255, 255, 0};
int plus1[] = {204, 255, 0};
int plus2[] = {153, 255, 0};
int plus3[] = {102, 255, 0};
int plus4[] = {51, 255, 0};
int plus5[] = {0, 255, 0};


// WiFi Credentials
const char* ssid = "WiFi SSID";
const char* password =  "PASSWORD";

String article;
int sentiment = 0;

void getSentiment() {

  HTTPClient http;

  http.begin("http://api.meaningcloud.com/sentiment-2.1?key=<<API-KEY>>&of=json&txt=" + article + "&lang=en");

  int httpCode = http.GET();

  if (httpCode == 200) {

    String payload = http.getString();

    const size_t capacity = 2*JSON_ARRAY_SIZE(0) + 7*JSON_ARRAY_SIZE(1) + 5*JSON_ARRAY_SIZE(2) + 2*JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(4) + 2*JSON_ARRAY_SIZE(5) + 2*JSON_ARRAY_SIZE(6) + JSON_ARRAY_SIZE(7) + 25*JSON_OBJECT_SIZE(4) + 4*JSON_OBJECT_SIZE(6) + 22*JSON_OBJECT_SIZE(7) + JSON_OBJECT_SIZE(8) + 2*JSON_OBJECT_SIZE(9) + 3*JSON_OBJECT_SIZE(10) + JSON_OBJECT_SIZE(11) + 3500;

    DynamicJsonDocument doc(capacity);

    DeserializationError error = deserializeJson(doc, payload, DeserializationOption::NestingLimit(11));

    if (error) {
      Serial.print(F("deserializeJson() Error: "));
      Serial.println(error.c_str());
      return;
    }

    JsonObject status = doc["status"];
    String score_tag = doc["score_tag"];

    Serial.println(score_tag);

    if (score_tag == "P+") {
      sentiment += 2;
    }
    else if (score_tag == "P") {
      sentiment += 1;
    }
    else if (score_tag == "N") {
      sentiment -= 1;
    }
    else if (score_tag == "N+") {
      sentiment -= 2;
    }
  }

  else {
    Serial.println("Bad HTTP Request");
  }
  http.end();
}

void setup() {
  pixels.begin();
  pixels.setBrightness(175);

  pinMode (14, OUTPUT); //LED Pin


  Serial.begin(115200);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting...");
  }
  Serial.println("Connected!");
}

void () {

  //reset sentiment count
  sentiment = 0;

  if ((WiFi.status() == WL_CONNECTED)) {

    HTTPClient http;

    http.begin("http://newsapi.org/v2/top-headlines?country=us&apiKey=<<API-KEY>>");

    int httpCode = http.GET();

    if (httpCode == 200) {

      String payload = http.getString();

      const size_t capacity = JSON_ARRAY_SIZE(20) + 20 * JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 20 * JSON_OBJECT_SIZE(8) + 16600;
      DynamicJsonDocument doc(capacity);

      DeserializationError error = deserializeJson(doc, payload);

      if (error) {
        Serial.print(F("deserializeJson() Error: "));
        Serial.println(error.c_str());
        return;
      }

      //Save number of headlines
      int noHeadlines = doc["totalResults"];

      //Loop through headlines & get sentiment
      for (int i = 0; i < 15; i++) {

        //Get Headlines from Newsapi
        JsonArray articles = doc["articles"];

        JsonObject articles_number = articles[i];

        const char* articles_number_title = articles_number["title"];

        article = String(articles_number_title);
        Serial.println(article);
        
        article.replace(" ", "%20");
        Serial.println(article);

        getSentiment();
      }
      
      Serial.println("Loop done.");
      Serial.println(sentiment);

      //Show state on NeoPixel
      if (sentiment <= -5) {
        for (int i = 0; i < 12; i++) {
          pixels.setPixelColor(i, minus5[0], minus5[1], minus5[2]);
          pixels.show();
        }
      } else if (sentiment >= 5) {
        for (int i = 0; i < 12; i++) {
          pixels.setPixelColor(i, plus5[0], plus5[1], plus5[2]);
          pixels.show();
        }
      }

      switch (sentiment) {
        case -4:
          for (int i = 0; i < 12; i++) {
            pixels.setPixelColor(i, minus4[0], minus4[1], minus4[2]);
            pixels.show();
          }
          break;
        case -3:
          for (int i = 0; i < 12; i++) {
            pixels.setPixelColor(i, minus3[0], minus3[1], minus3[2]);
            pixels.show();
          }
          break;
        case -2:
          for (int i = 0; i < 12; i++) {
            pixels.setPixelColor(i, minus2[0], minus2[1], minus2[2]);
            pixels.show();
          }
          break;
        case -1:
          for (int i = 0; i < 12; i++) {
            pixels.setPixelColor(i, minus1[0], minus1[1], minus1[2]);
            pixels.show();
          }
          break;
        case 0:
          for (int i = 0; i < 12; i++) {
            pixels.setPixelColor(i, neutral[0], neutral[1], neutral[2]);
            pixels.show();
          }
          break;
        case 1:
          for (int i = 0; i < 12; i++) {
            pixels.setPixelColor(i, plus1[0], plus1[1], plus1[2]);
            pixels.show();
          }
          break;
        case 2:
          for (int i = 0; i < 12; i++) {
            pixels.setPixelColor(i, plus2[0], plus2[1], plus2[2]);
            pixels.show();
          }
          break;
        case 3:
          for (int i = 0; i < 12; i++) {
            pixels.setPixelColor(i, plus3[0], plus3[1], plus3[2]);
            pixels.show();
          }
          break;
        case 4:
          for (int i = 0; i < 12; i++) {
            pixels.setPixelColor(i, plus4[0], plus4[1], plus4[2]);
            pixels.show();
          }
          break;
      }
    }

    else {
      Serial.println("Bad HTTP Request");
    }

    http.end();
    Serial.println("Connection closed.");
  }
  //Wait for 30 minutes until next call
  delay(1800000);
}

Wie geht es weiter?

Hast du Lust auf mehr Daten und mehr Beleuchtung? In diesem Projekt baust du einen Würfel, der beginnt zu leuchten, wenn die International Space Station über ihm fliegt.

]]>