.. include:: /index.rst
:start-after: start_hello_message
:end-before: end_hello_message
.. _py_smart_weather_station:
(Beispiel) Intelligente Wetterstation
=============================================
**Einführung**
Dieses Projekt erstellt eine umfassende **intelligente Wetterstation**, die lokale Umweltsensoren mit globalen Wetterdaten und AI-Analyse kombiniert. Das System integriert:
1. **Lokale Sensordaten** von DHT11 (Temperatur/Luftfeuchtigkeit) und LDR (Lichtsensor)
2. **Globale Wettervorhersagen** über die OpenWeather-API
3. **AI-gestützte Sprachanalyse** mit den GPT- und TTS-Funktionen von OpenAI
4. **Visuelle Anzeige** auf einem 128x64-OLED-Display
5. **Interaktive Taste** für spontane, AI-generierte Wetterauswertungen
.. raw:: html
Die Wetterstation vergleicht automatisch lokale Bedingungen mit Vorhersagedaten und liefert intelligente Empfehlungen per Sprachausgabe. So entsteht eine vollständige Lösung zur Umgebungsüberwachung.
Sie können auch andere LLM- und TTS-Module verwenden, um Ihre eigenen intelligenten Geräte zu entwickeln.
Siehe:
* :ref:`py_online_llm`
* :ref:`tts_espeak_pico2wave`
* :ref:`tts_piper_openai`
----------------------------------------------
**Was Sie benötigen**
Für dieses Projekt werden die folgenden Komponenten benötigt:
.. list-table::
:widths: 30 20
:header-rows: 1
* - COMPONENT
- PURCHASE LINK
* - :ref:`cpn_humiture_sensor`
- |link_humiture_buy|
* - :ref:`cpn_photoresistor`
- |link_photoresistor_buy|
* - :ref:`cpn_button`
- |link_button_buy|
* - :ref:`cpn_oled`
- \-
* - :ref:`cpn_fusion_hat`
- \-
* - :ref:`cpn_wires`
- |link_wires_buy|
* - :ref:`cpn_resistor`
- |link_resistor_buy|
* - Raspberry Pi
- \-
----------------------------------------------
**Schaltplan**
Verbinden Sie die Komponenten wie folgt mit dem Fusion HAT+:
.. image:: img/fzz/llm_weather_bb.png
:width: 80%
:align: center
----------------------------------------------
.. include:: python_online_llms.rst
:start-after: start_setup_openai
:end-before: end_setup_openai
---------------------------------------------
**OpenWeather-API-Schlüssel abrufen**
|link_openweather| ist ein Onlinedienst der OpenWeather Ltd, der über eine API weltweite Wetterdaten bereitstellt, darunter aktuelle Wetterdaten, Vorhersagen, Nowcasts und historische Wetterdaten für jeden geografischen Ort.
#. Besuchen Sie |link_openweather| und melden Sie sich an bzw. erstellen Sie ein Konto.
.. image:: img/OWM-1.png
#. Öffnen Sie über die Navigationsleiste die API-Seite.
.. image:: img/OWM-2.png
#. Suchen Sie **Current Weather Data** und klicken Sie auf **Subscribe**.
.. image:: img/OWM-3.png
#. Abonnieren Sie unter **Current weather and forecasts collection** den passenden Dienst. Für unser Projekt reicht die kostenlose Version völlig aus.
.. image:: img/OWM-4.png
#. Kopieren Sie den Schlüssel von der Seite **API keys**.
.. image:: img/OWM-5.png
#. Öffnen Sie die Datei ``secret.py`` mit folgendem Befehl:
.. raw:: html
.. code-block:: bash
cd ~/ai-lab-kit/llm
sudo nano secret.py
#. Fügen Sie den kopierten API-Schlüssel hinzu:
.. code-block:: shell
:emphasize-lines: 1
OPENWEATHER_API_KEY = "732exxxxxxxxxxxxxxxxxxxxx919b"
#. Drücken Sie ``Ctrl + X``, ``Y`` und anschließend ``Enter``, um die Datei zu speichern und den Editor zu beenden.
---------------------------------------------
**Beispiel ausführen**
#. Code ausführen
.. raw:: html
.. code-block:: shell
cd ~/ai-lab-kit/llm
sudo python3 llm_openai_weather.py
#. Was Sie nach dem Start des Skripts sehen
* Das OLED schaltet sich ein und beginnt, Wetterinformationen anzuzeigen.
* Das Programm gibt im Terminal Startinformationen aus, darunter die Zielstadt und den verwendeten Button-Pin.
* Das OLED wechselt automatisch alle 10 Sekunden zwischen den Seiten (insgesamt 3 Seiten):
- **Seite 1: Lokale Sensoren** (DHT11 + LDR)
Zeigt lokale Temperatur, Luftfeuchtigkeit und Lichtstärke an (einschließlich einer kleinen Lichtbalkenanzeige).
- **Seite 2: Wettervorhersage** (OpenWeather)
Zeigt die aktuelle Temperatur, die Wetterbeschreibung und die letzte Aktualisierungszeit an.
- **Seite 3: AI-Auswertung**
Zeigt die Unterschiede zwischen den lokalen Sensordaten und den OpenWeather-Daten sowie einen einfachen Komfortstatus an (z. B. Comfortable / Warm / Cool / Humid / Dry).
#. AI-Sprachanalyse auslösen
Drücken Sie die Taste an **GPIO 27**, damit die AI eine kurze Analyse im Stil eines „Wetterreporters“ erstellt.
* Im Terminal wird ein Abschnitt ``AI Analysis`` ausgegeben, einschließlich:
- Lokale Messwerte (Temperatur / Luftfeuchtigkeit / Licht)
- Externe Wetterdaten (OpenWeather-Temperatur + Beschreibung)
- Eine kurze, von der AI erzeugte Zusammenfassung
* Das OLED zeigt vorübergehend **SPEAKING...**
* Die Analyse wird über den Lautsprecher mit OpenAI TTS gesprochen
#. Aktualisierungsverhalten der Daten
* Die lokalen Sensoren werden etwa alle **2 Sekunden** aktualisiert.
* Die OpenWeather-Daten werden etwa alle **5 Minuten** aktualisiert.
* Der Lichtwert wird automatisch geglättet, um Flackern zu reduzieren.
#. Programm beenden
* Drücken Sie im Terminal ``Ctrl+C``, um das Programm zu beenden.
* Das OLED wird gelöscht und das Programm wird sicher beendet.
----------------------------------------------
**Code**
Hier ist das vollständige Python-Skript für die intelligente Wetterstation:
.. raw:: html
.. code-block:: python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Smart Weather Station with AI Assistant
- Reads local temperature & humidity from DHT11 on GPIO 17
- Reads light level from LDR on ADC A0 (0..4095)
- Fetches weather forecast from OpenWeather API
- Provides AI voice analysis using OpenAI (triggered by button)
- Displays all information on 128x64 SSD1306 OLED
"""
import time
import requests
from datetime import datetime
from statistics import mean
from fusion_hat.modules import DHT11
from fusion_hat.adc import ADC
from fusion_hat.pin import Pin, Mode, Pull
from PIL import Image, ImageDraw, ImageFont
import adafruit_ssd1306
import board
from sunfounder_voice_assistant.tts import OpenAI_TTS
from secret import OPENAI_API_KEY, OPENWEATHER_API_KEY
from signal import pause
# Configuration
DHT_PIN = 17 # DHT11 uses GPIO 17
LDR_CH = 0
I2C_ADDR = 0x3C
# OpenWeather API Configuration
CITY_NAME = "Shanghai"
COUNTRY_CODE = "CN"
LATITUDE = 31.2304
LONGITUDE = 121.4737
UNITS = "metric"
# Update intervals in seconds
WEATHER_UPDATE_INTERVAL = 300
SENSOR_UPDATE_INTERVAL = 2
# GPIO Pins
BUTTON_PIN = 27 # Button uses GPIO 27
# OLED Setup
WIDTH, HEIGHT = 128, 64
i2c = board.I2C()
oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=I2C_ADDR)
oled.fill(0)
oled.show()
# Load fonts
try:
font_small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 10)
font_medium = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 12)
font_large = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14)
except:
print("Warning: Using default font")
font_small = ImageFont.load_default()
font_medium = ImageFont.load_default()
font_large = ImageFont.load_default()
image = Image.new("1", (WIDTH, HEIGHT))
draw = ImageDraw.Draw(image)
# Sensors
dht = DHT11(pin=DHT_PIN)
ldr = ADC(LDR_CH)
# Button for triggering AI analysis
button = Pin(BUTTON_PIN, mode=Mode.IN, pull=Pull.DOWN)
# OpenWeather API Class
class WeatherAPI:
def __init__(self, api_key, city, country_code, lat=None, lon=None):
self.api_key = api_key
self.city = city
self.country_code = country_code
self.lat = lat
self.lon = lon
self.current_weather = None
self.forecast = None
self.last_update = 0
def get_weather_url(self):
if self.lat and self.lon:
return f"https://api.openweathermap.org/data/2.5/weather?lat={self.lat}&lon={self.lon}&appid={self.api_key}&units={UNITS}"
else:
return f"https://api.openweathermap.org/data/2.5/weather?q={self.city},{self.country_code}&appid={self.api_key}&units={UNITS}"
def get_forecast_url(self):
if self.lat and self.lon:
return f"https://api.openweathermap.org/data/2.5/forecast?lat={self.lat}&lon={self.lon}&appid={self.api_key}&units={UNITS}"
else:
return f"https://api.openweathermap.org/data/2.5/forecast?q={self.city},{self.country_code}&appid={self.api_key}&units={UNITS}"
def update_weather(self):
try:
# Current weather
response = requests.get(self.get_weather_url(), timeout=10)
if response.status_code == 200:
self.current_weather = response.json()
print(f"Weather API success: {self.current_weather['weather'][0]['description']}")
else:
print(f"Weather API error: {response.status_code}")
return False
# Forecast
response = requests.get(self.get_forecast_url(), timeout=10)
if response.status_code == 200:
self.forecast = response.json()
self.last_update = time.time()
return True
except Exception as e:
print(f"Weather API error: {e}")
return False
def get_temperature(self):
if self.current_weather:
return self.current_weather['main']['temp']
return None
def get_humidity(self):
if self.current_weather:
return self.current_weather['main']['humidity']
return None
def get_weather_description(self):
if self.current_weather:
return self.current_weather['weather'][0]['description'].title()
return None
def get_weather_condition(self):
if self.current_weather:
weather_id = self.current_weather['weather'][0]['id']
if weather_id < 300:
return "TSTORM"
elif weather_id < 600:
return "RAIN"
elif weather_id < 700:
return "SNOW"
elif weather_id == 800:
return "CLEAR"
elif weather_id < 900:
return "CLOUDS"
else:
return "OTHER"
return "N/A"
def get_forecast_summary(self):
if not self.forecast or 'list' not in self.forecast:
return "No forecast"
forecasts = self.forecast['list'][:8]
temps = [f['main']['temp'] for f in forecasts]
min_temp = min(temps)
max_temp = max(temps)
conditions = {}
for f in forecasts:
condition = f['weather'][0]['main']
conditions[condition] = conditions.get(condition, 0) + 1
most_common = max(conditions, key=conditions.get)
return f"{min_temp:.0f}-{max_temp:.0f}C {most_common}"
# AI Weather Analyst Class
class WeatherAI:
def __init__(self, api_key):
self.api_key = api_key
self.tts = OpenAI_TTS(api_key=api_key)
self.tts.set_voice(self.tts.Voice.ALLOY)
self.is_speaking = False
def analyze_weather(self, local_temp, local_hum, local_light, weather_data):
temp_diff = abs(local_temp - weather_data.get('current_temp', local_temp)) if weather_data.get('current_temp') else 0
if temp_diff > 3:
accuracy = "significantly different from"
elif temp_diff > 1:
accuracy = "slightly different from"
else:
accuracy = "very close to"
recommendations = []
if local_hum > 80:
recommendations.append("It's quite humid")
elif local_hum < 30:
recommendations.append("The air is dry")
if local_light > 80:
recommendations.append("It's bright here")
elif local_light < 20:
recommendations.append("It's quite dark")
weather_desc = weather_data.get('weather_desc', '').lower()
if 'rain' in weather_desc or 'drizzle' in weather_desc or 'thunderstorm' in weather_desc:
recommendations.append("Don't forget an umbrella")
elif 'clear' in weather_desc:
recommendations.append("Great day to go outside")
elif 'cloud' in weather_desc:
recommendations.append("Partly cloudy today")
rec_text = ". ".join(recommendations) + "." if recommendations else "Conditions are normal."
analysis = f"Local sensors show {local_temp:.1f}C, which is {accuracy} the forecast. {rec_text}"
return analysis
def speak_analysis(self, analysis_text):
if self.is_speaking:
print("Already speaking, please wait...")
return False
try:
self.is_speaking = True
print(f"Speaking analysis: {analysis_text}")
self.tts.say(analysis_text, instructions="speak clearly and professionally like a weather reporter")
self.is_speaking = False
return True
except Exception as e:
print(f"TTS error: {e}")
self.is_speaking = False
return False
# Light sensor helper
_light_window = []
def light_percent(raw, min_val=0, max_val=4095):
global _light_window
_light_window.append(raw)
if len(_light_window) > 5:
_light_window.pop(0)
smooth_raw = int(mean(_light_window))
pct = (smooth_raw - min_val) / (max_val - min_val) * 100 if max_val > min_val else 50
pct = max(0, min(100, pct))
return int(pct), smooth_raw
# Display Manager Class
class DisplayManager:
def __init__(self):
self.current_page = 0
self.num_pages = 3
self.last_page_change = 0
self.page_cycle_interval = 10
def next_page(self):
self.current_page = (self.current_page + 1) % self.num_pages
self.last_page_change = time.time()
def should_change_page(self):
return time.time() - self.last_page_change > self.page_cycle_interval
def draw_page(self, page_num, local_temp, local_hum, light_pct, weather_api, weather_ai):
draw.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0)
if page_num == 0:
self._draw_local_sensors(local_temp, local_hum, light_pct)
elif page_num == 1:
self._draw_weather_forecast(weather_api)
elif page_num == 2:
self._draw_ai_insights(local_temp, local_hum, light_pct, weather_api)
# Page indicator at bottom right
indicator = f"{page_num+1}/{self.num_pages}"
indicator_width = len(indicator) * 6
draw.text((WIDTH - indicator_width - 2, HEIGHT - 10), indicator, font=font_small, fill=255)
def _draw_local_sensors(self, temp, hum, light):
# Title at top
draw.text((2, 2), "LOCAL SENSORS", font=font_medium, fill=255)
# Temperature - larger font on first line
temp_text = f"Temp: {temp:.1f} C"
draw.text((10, 18), temp_text, font=font_large, fill=255)
# Humidity - second line
hum_text = f"Humidity: {hum:.1f}%"
draw.text((10, 38), hum_text, font=font_medium, fill=255)
# Light with bar on same line
light_text = f"Light: {light}%"
draw.text((10, 53), light_text, font=font_small, fill=255)
# Light bar positioned next to text, not overlapping
bar_x = 60 # Position after "Light: XX%"
bar_y = 55
bar_width = 50
bar_height = 4
# Draw background bar
draw.rectangle((bar_x, bar_y, bar_x + bar_width, bar_y + bar_height), outline=255, fill=0)
# Draw filled portion
fill_width = int(bar_width * light / 100)
if fill_width > 0:
draw.rectangle((bar_x, bar_y, bar_x + fill_width, bar_y + bar_height), outline=0, fill=255)
def _draw_weather_forecast(self, weather_api):
draw.text((2, 2), "WEATHER", font=font_medium, fill=255)
if not weather_api.current_weather:
draw.text((10, 25), "No weather data", font=font_medium, fill=255)
draw.text((10, 45), "Check connection", font=font_small, fill=255)
return
current_temp = weather_api.get_temperature()
weather_desc = weather_api.get_weather_description()
weather_cond = weather_api.get_weather_condition()
# Temperature - large font
if current_temp is not None:
draw.text((10, 18), f"{current_temp:.0f} C", font=font_large, fill=255)
# Weather description
if weather_desc:
desc_text = weather_desc[:15]
draw.text((10, 38), desc_text, font=font_medium, fill=255)
# Weather condition
if weather_cond:
draw.text((10, 53), weather_cond, font=font_small, fill=255)
# Update time at top right
if weather_api.last_update > 0:
update_time = datetime.fromtimestamp(weather_api.last_update).strftime("%H:%M")
update_text = f"Up: {update_time}"
update_width = len(update_text) * 6
draw.text((WIDTH - update_width - 2, 2), update_text, font=font_small, fill=255)
def _draw_ai_insights(self, local_temp, local_hum, light_pct, weather_api):
draw.text((2, 2), "AI INSIGHTS", font=font_medium, fill=255)
api_temp = weather_api.get_temperature() if weather_api.current_weather else None
api_hum = weather_api.get_humidity() if weather_api.current_weather else None
line_y = 18
# Temperature difference
if api_temp is not None:
temp_diff = local_temp - api_temp
temp_symbol = "+" if temp_diff > 0 else "" if temp_diff == 0 else ""
diff_text = f"Temp: {temp_symbol}{temp_diff:.1f}C"
draw.text((10, line_y), diff_text, font=font_medium, fill=255)
line_y += 15
# Humidity difference
if api_hum is not None:
hum_diff = local_hum - api_hum
hum_symbol = "+" if hum_diff > 0 else "" if hum_diff == 0 else ""
diff_text = f"Hum: {hum_symbol}{hum_diff:.1f}%"
draw.text((10, line_y), diff_text, font=font_medium, fill=255)
line_y += 15
# Comfort level
comfort = "Normal"
comfort_color = 255
if 20 <= local_temp <= 25 and 40 <= local_hum <= 60:
comfort = "Comfortable"
comfort_color = 255
elif local_temp > 28:
comfort = "Warm"
comfort_color = 255
elif local_temp < 16:
comfort = "Cool"
comfort_color = 255
elif local_hum > 70:
comfort = "Humid"
comfort_color = 255
elif local_hum < 30:
comfort = "Dry"
comfort_color = 255
draw.text((10, line_y), f"Feel: {comfort}", font=font_small, fill=comfort_color)
# Button hint at bottom left
draw.text((2, HEIGHT - 10), "Press BTN for AI", font=font_small, fill=255)
# Main Application Class
class SmartWeatherStation:
def __init__(self):
print("Initializing Smart Weather Station...")
self.weather_api = WeatherAPI(OPENWEATHER_API_KEY, CITY_NAME, COUNTRY_CODE, LATITUDE, LONGITUDE)
self.weather_ai = WeatherAI(OPENAI_API_KEY)
self.display = DisplayManager()
self.local_temp = 0.0
self.local_hum = 0.0
self.light_pct = 0
self.raw_adc = 0
self.last_weather_update = 0
self.last_sensor_update = 0
# Setup button callback
button.when_activated = self._button_pressed
# Initial readings
self._update_sensors()
self.weather_api.update_weather()
print("Smart Weather Station ready!")
print(f"City: {CITY_NAME}")
print(f"Temperature unit: {UNITS}")
print(f"Button on GPIO {BUTTON_PIN} for AI analysis")
print("="*50)
def _update_sensors(self):
try:
result = dht.read()
if result:
hum, temp = result
self.local_hum = float(hum)
self.local_temp = float(temp)
raw = ldr.read()
self.light_pct, self.raw_adc = light_percent(raw)
self.last_sensor_update = time.time()
return True
except Exception as e:
print(f"Sensor error: {e}")
return False
def _update_weather(self):
if time.time() - self.last_weather_update > WEATHER_UPDATE_INTERVAL:
print("Updating weather data...")
if self.weather_api.update_weather():
self.last_weather_update = time.time()
return True
return False
def _button_pressed(self):
"""Called when button is pressed"""
print("\n" + "="*50)
print("Button pressed! Triggering AI analysis...")
print("="*50)
# Update sensors first to get latest data
self._update_sensors()
# Get weather data
api_temp = self.weather_api.get_temperature()
if api_temp is None:
print("No weather data available. Please wait for update.")
return
# Prepare weather data for analysis
weather_data = {
'current_temp': api_temp,
'weather_desc': self.weather_api.get_weather_description(),
'forecast_summary': self.weather_api.get_forecast_summary()
}
# Generate analysis
analysis = self.weather_ai.analyze_weather(
self.local_temp,
self.local_hum,
self.light_pct,
weather_data
)
print(f"\nAI Analysis:")
print(f"Local: {self.local_temp:.1f}C, {self.local_hum:.1f}%, Light: {self.light_pct}%")
print(f"Remote: {api_temp}C, {self.weather_api.get_weather_description()}")
print(f"Analysis: {analysis}")
# Show "Speaking..." on display
self._show_speaking_message()
# Speak the analysis
success = self.weather_ai.speak_analysis(analysis)
if success:
print("Analysis completed successfully!")
else:
print("Analysis failed or interrupted.")
print("="*50)
def _show_speaking_message(self):
"""Display a temporary "Speaking..." message"""
draw.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0)
draw.text((WIDTH//2 - 40, HEIGHT//2 - 10), "SPEAKING...", font=font_medium, fill=255)
oled.image(image)
oled.show()
def run(self):
print("\n" + "="*50)
print("SMART WEATHER STATION")
print("="*50)
print("Display Pages:")
print("1. Local Sensors (DHT11 + LDR)")
print("2. Weather Forecast (OpenWeather)")
print("3. AI Insights (Comparisons)")
print("")
print(f"Press button on GPIO {BUTTON_PIN} for AI voice analysis")
print("Press Ctrl+C to exit")
print("="*50 + "\n")
try:
while True:
current_time = time.time()
# Update sensors periodically
if current_time - self.last_sensor_update > SENSOR_UPDATE_INTERVAL:
self._update_sensors()
# Update weather data periodically
self._update_weather()
# Auto-cycle display pages
if self.display.should_change_page():
self.display.next_page()
# Draw current page
self.display.draw_page(
self.display.current_page,
self.local_temp,
self.local_hum,
self.light_pct,
self.weather_api,
self.weather_ai
)
# Update OLED display
oled.image(image)
oled.show()
# Small delay to prevent CPU overload
time.sleep(0.1)
except KeyboardInterrupt:
print("\nShutting down...")
finally:
# Cleanup
oled.fill(0)
oled.show()
print("Smart Weather Station stopped.")
# Main Entry Point
if __name__ == "__main__":
if not OPENAI_API_KEY or OPENAI_API_KEY == "your-openai-api-key-here":
print("ERROR: Please set your OpenAI API key in secret.py")
exit(1)
if not OPENWEATHER_API_KEY or OPENWEATHER_API_KEY == "your-openweather-api-key-here":
print("ERROR: Please set your OpenWeather API key in secret.py")
print("Get one at: https://openweathermap.org/api")
exit(1)
station = SmartWeatherStation()
station.run()
----------------------------------------------
**Code verstehen**
1. Sensorintegration
Das System liest Daten von zwei lokalen Sensoren aus:
.. code-block:: python
# DHT11 für Temperatur und Luftfeuchtigkeit
dht = DHT11(pin=DHT_PIN)
result = dht.read() # Gibt (humidity, temperature) zurück
# LDR (lichtabhängiger Widerstand) über ADC
ldr = ADC(LDR_CH)
raw = ldr.read() # Gibt einen Wert von 0–4095 zurück
2. OpenWeather-API-Integration
Die Klasse ``WeatherAPI`` verwaltet die Verbindung zu OpenWeather für aktuelle Wetterbedingungen und Vorhersagen:
.. code-block:: python
class WeatherAPI:
def update_weather(self):
# Endpoint für aktuelles Wetter
response = requests.get(self.get_weather_url(), timeout=10)
self.current_weather = response.json()
# Endpoint für Vorhersage
response = requests.get(self.get_forecast_url(), timeout=10)
self.forecast = response.json()
3. AI-Analyse-Engine
Die Klasse ``WeatherAI`` erzeugt intelligente Wetteranalysen und wandelt diese in Sprache um:
.. code-block:: python
class WeatherAI:
def analyze_weather(self, local_temp, local_hum, local_light, weather_data):
# Temperaturdifferenz berechnen
temp_diff = abs(local_temp - weather_data.get('current_temp', local_temp))
# Empfehlungen basierend auf den Bedingungen erstellen
recommendations = []
if local_hum > 80:
recommendations.append("It's quite humid")
# Analyse-Text erstellen
analysis = f"Local sensors show {local_temp:.1f}C..."
return analysis
def speak_analysis(self, analysis_text):
self.tts.say(analysis_text, instructions="speak clearly...")
4. Mehrseitiges Display-System
Der ``DisplayManager`` verwaltet drei Informationsseiten, die automatisch wechseln:
.. code-block:: python
class DisplayManager:
def draw_page(self, page_num, ...):
if page_num == 0:
self._draw_local_sensors(...)
elif page_num == 1:
self._draw_weather_forecast(...)
elif page_num == 2:
self._draw_ai_insights(...)
def _draw_local_sensors(self, temp, hum, light):
# Temperatur, Luftfeuchtigkeit und Lichtstärke mit Fortschrittsbalken zeichnen
5. Button-Ereignisverarbeitung
Beim Drücken der Taste wird eine AI-Sprachanalyse ausgelöst:
.. code-block:: python
button = Pin(BUTTON_PIN, mode=Mode.IN, pull=Pull.DOWN)
button.when_activated = self._button_pressed
def _button_pressed(self):
# Sensoren aktualisieren, Analyse erstellen und sprechen
analysis = self.weather_ai.analyze_weather(...)
self.weather_ai.speak_analysis(analysis)
6. Daten-Glättung für den Lichtsensor
Der Lichtsensor verwendet einen gleitenden Mittelwert für stabilere Messwerte:
.. code-block:: python
def light_percent(raw, min_val=0, max_val=4095):
_light_window.append(raw)
if len(_light_window) > 5:
_light_window.pop(0)
smooth_raw = int(mean(_light_window)) # Gleitender Durchschnitt
pct = (smooth_raw - min_val) / (max_val - min_val) * 100
7. Hauptanwendungsschleife
Die Klasse ``SmartWeatherStation`` koordiniert alle Komponenten mit korrektem Timing:
.. code-block:: python
def run(self):
while True:
# Sensoren alle 2 Sekunden aktualisieren
if time.time() - self.last_sensor_update > SENSOR_UPDATE_INTERVAL:
self._update_sensors()
# Wetterdaten alle 5 Minuten aktualisieren
self._update_weather()
# Seiten alle 10 Sekunden automatisch wechseln
if self.display.should_change_page():
self.display.next_page()
# Aktuelle Seite anzeigen
self.display.draw_page(...)
----------------------------------------------
**Fehlerbehebung**
- Fehler „DHT11 read failed“
- Verkabelung prüfen: VCC (3.3V), DATA (GPIO 17), GND
- Einen **10kΩ Pull-up-Widerstand** zwischen DATA und VCC hinzufügen
- Sensor von Wärmequellen fernhalten (Raspberry Pi kann selbst Wärme erzeugen)
- Kleine Verzögerung zwischen Messungen einfügen: ``time.sleep(2)``
- OpenWeather-API-Fehler
- Prüfen, ob der API-Schlüssel korrekt und gültig ist
- Internetverbindung testen: ``ping 8.8.8.8``
- Stadtname und Ländercode überprüfen
- Kostenlose API hat Limits (60 Aufrufe/Minute, 1.000.000 Aufrufe/Monat)
- OLED-Display zeigt nichts
- I2C-Verbindung prüfen: ``sudo i2cdetect -y 1`` (sollte **0x3C** anzeigen)
- Prüfen, ob das OLED mit Strom versorgt wird (3.3V oder 5V je nach Modell)
- Richtige I2C-Adresse sicherstellen (0x3C oder 0x3D)
- Kein Ton bei TTS
- Audioausgabe prüfen: ``sudo raspi-config`` → **System Options → Audio**
- Audiotest durchführen: ``speaker-test -t sine -f 440``
- Prüfen, ob der OpenAI-TTS-API-Schlüssel genügend Credits hat
- Internetverbindung für API-Aufrufe prüfen
- Button reagiert nicht
- Verkabelung prüfen: Taste zwischen **GPIO 27** und **GND**
- Prüfen, ob Pull-down-Widerstand im Code aktiviert ist
- Taste mit einfachem Testskript überprüfen
- Ungenaue Lichtmessung
- LDR kalibrieren, indem ``min_val`` und ``max_val`` in ``light_percent()`` angepasst werden
- LDR vollständig abdecken, um den Minimalwert zu bestimmen
- LDR hellem Licht aussetzen, um den Maximalwert zu bestimmen
- Sicherstellen, dass der LDR nicht im Schatten anderer Bauteile liegt
- Wetterdaten veraltet
- ``WEATHER_UPDATE_INTERVAL`` erhöhen, um häufiger zu aktualisieren
- Prüfen, ob API-Aufrufe erfolgreich sind (Fehlermeldungen beachten)
- Systemzeit prüfen: ``date``
----------------------------------------------
Diese intelligente Wetterstation zeigt, wie **lokale Sensordaten**, **Cloud-Dienste** und **AI** kombiniert werden können, um ein fortschrittliches Umgebungsüberwachungssystem zu schaffen, das praktische Erkenntnisse und intelligente Empfehlungen liefert.