研究1:温湿度計モジュールの活用とAPIでの当日の外気温・明日の天気予報の取得と可視化

Hayate.Labに戻る

【実現したいこと(Must要件)】
  ①年月日曜日が外部モニターに出力されていること
  ②現在の天気状態が外部モニターに出力されていること
  ③外気温と室内気温・室内湿度が外部モニターに出力されていること
  ④翌日の天気予報と最高気温が外部モニターに出力されていること
  ⑤画面右半分は写真(アルバム)を一定時間毎に切り替えて外部モニターに出力されていること
  ⑥画面出力は全画面で描画すること

上記6つについて、どのように実装するべきかを検討します。

 ①について:
   →Pythonのdatetimeライブラリを使用して年月日曜日を取得する方向で実装する。

 ②について:
   →『OpenWeatherMap』という全世界の天気情報がAPIで提供されているサイトを活用する。
    “1,000 API calls per day for free”とのことなので、無料枠内でAPIをcallするように設計。
     ※APIキー、地区、APIコール文を予め個々人で準備しておく必要あり。

 ③について:
   →外気温については、②で用いたAPIで取得する。
    室内温度・室内湿度は DHT22 というモジュールで一定時間毎に取得する。
    取得方法は、下記のサイトを参照のこと。
     外部サイト: Rasperry Pi に DHT22 を接続して温度・湿度を計測する

 ④について:
   →翌日の天気予報は②で用いたAPIで取得する。

 ⑤について:
   →外部ディスプレイに合わせた形で描画位置を指定する。

 ⑥について:
   →pythonのpygameライブラリを用いて実装する。

以上をプログラムコードに書き起こすと下記のようになります。

使用しているフォント名:Buildingsandundertherailwaytracks-Regular.otf

import pygame
import requests
from datetime import datetime, timedelta
import os
import board
import adafruit_dht

# 天気APIの設定(例としてOpenWeatherMapを使用)
API_KEY = 'あなたのAPIキーを入力してください。'
CITY = 'Tokyo'
URL_CURRENT = f'http://api.openweathermap.org/data/2.5/weather?q={CITY}&appid={API_KEY}&units=metric'
URL_FORECAST = f'http://api.openweathermap.org/data/2.5/forecast?q={CITY}&appid={API_KEY}&units=metric'

# 画像のパスをリストに格納
image_paths = [f'custom_image_{i}.jpg' for i in range(1, 61)]

# Pygameの初期化
pygame.init()
screen = pygame.display.set_mode((800, 480), pygame.FULLSCREEN)
pygame.display.set_caption('Raspberry Pi Display')

# フォントのロード
font_path = "Buildingsandundertherailwaytracks-Regular.otf"  # 日本語フォントのパスを指定
small_font = pygame.font.Font(font_path, 30)
large_font = pygame.font.Font(font_path, 45)

# Initial the DHT device, with data pin connected to:
dhtDevice = adafruit_dht.DHT22(board.D18)

def fetch_weather():
    try:
        # 現在の天気を取得
        response_current = requests.get(URL_CURRENT)
        response_current.raise_for_status()
        data_current = response_current.json()
        current_weather = data_current['weather'][0]['main']
        current_temp = data_current['main']['temp']
        
        # 予報(翌日の天気)を取得
        response_forecast = requests.get(URL_FORECAST)
        response_forecast.raise_for_status()
        data_forecast = response_forecast.json()
        
        # 翌日の予報を取得
        daily_weather = None
        daily_max_temp = None
        max_temp = -273.15  # Initialize with a very low temperature
        for forecast in data_forecast['list']:
            forecast_time = datetime.fromtimestamp(forecast['dt'])
            if forecast_time.date() == (datetime.now() + timedelta(days=1)).date():
                temp = forecast['main']['temp_max']
                if temp > max_temp:
                    max_temp = temp
                    daily_weather = forecast['weather'][0]['main']
        daily_max_temp = max_temp
        
        return current_weather, current_temp, daily_weather, daily_max_temp
    except (requests.exceptions.RequestException, KeyError, IndexError) as e:
        print(f"Error fetching weather data: {e}")
        return "Unknown", 0.0, "Unknown", 0.0

def get_weather_icon(weather):
    icon_map = {
        'Clear': 'clear.png',
        'Rain': 'rain.png',
        'Clouds': 'clouds.png',
        'Drizzle': 'drizzle.png',
        'Thunderstorm': 'thunderstorm.png',
        'Snow': 'snow.png',
        'Mist': 'mist.png',
        'Smoke': 'smoke.png',
        'Haze': 'haze.png',
        'Dust': 'dust.png',
        'Fog': 'fog.png',
        'Sand': 'sand.png',
        'Ash': 'ash.png',
        'Squall': 'squall.png',
        'Tornado': 'tornado.png'
    }
    icon_path = icon_map.get(weather, None)
    if icon_path and os.path.exists(icon_path):
        icon_image = pygame.image.load(icon_path)
        return icon_image
    return None

def draw_text(surface, text, position, font, color=(0, 0, 0)):
    text_surface = font.render(text, True, color)
    surface.blit(text_surface, position)

def main():
    clock = pygame.time.Clock()
    start_time = datetime.now()
    last_weather_update = datetime.now() - timedelta(minutes=10)  # 初回すぐに更新されるように設定
    last_dht_update = datetime.now() - timedelta(minutes=10)  # 初回すぐに更新されるように設定
    current_image_index = 0
    current_weather, current_temp, daily_weather, daily_max_temp = fetch_weather()
    current_weather_icon = get_weather_icon(current_weather)
    daily_weather_icon = get_weather_icon(daily_weather)

    indoor_temp = 25.0
    humidity = 60

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                return
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    return

        # 画面をベージュでクリア
        screen.fill((245, 245, 220))

        # 現在の日時を取得
        now = datetime.now()
        date_str = now.strftime('%Y-%m-%d %A')
        time_str = now.strftime('%H:%M:%S')

        # 天気情報の取得(10分毎)
        if (now - last_weather_update).total_seconds() > 600:
            current_weather, current_temp, daily_weather, daily_max_temp = fetch_weather()
            current_weather_icon = get_weather_icon(current_weather)
            daily_weather_icon = get_weather_icon(daily_weather)
            last_weather_update = now

        # DHT22のデータを取得(1分毎)
        if (now - last_dht_update).total_seconds() > 60:
            try:
                indoor_temp = dhtDevice.temperature
                humidity = dhtDevice.humidity
            except RuntimeError as error:
                print(error.args[0])
            except Exception as error:
                dhtDevice.exit()
                raise error
            last_dht_update = now

        # 画面の左半分に描画
        draw_text(screen, date_str, (10, 10), small_font)
        draw_text(screen, time_str, (10, 50), large_font)
        
        if current_weather_icon:
            screen.blit(current_weather_icon, (10, 110))
            draw_text(screen, current_weather, (120, 120), large_font)
        else:
            draw_text(screen, current_weather, (10, 120), large_font)
        
        draw_text(screen, f'外気温: {current_temp}度', (10, 200), large_font)
        draw_text(screen, f'室内温度: {indoor_temp}度', (10, 250), large_font)
        draw_text(screen, f'{humidity}%', (230, 300), large_font)

        # 翌日の天気アイコンと最高気温を表示
        draw_text(screen, '翌日の天気:', (10, 350), small_font)
        if daily_weather_icon:
            screen.blit(daily_weather_icon, (10, 380))
            draw_text(screen, f'最高気温 {daily_max_temp}度', (125, 425), small_font)
        
        # 20秒毎に表示する画像を変更
        elapsed_time = (now - start_time).total_seconds()
        if elapsed_time > 20:
            current_image_index = (current_image_index + 1) % len(image_paths)
            start_time = now
        
        # 画像のロードと描画
        if os.path.exists(image_paths[current_image_index]):
            custom_image = pygame.image.load(image_paths[current_image_index])
            custom_image_rect = custom_image.get_rect(center=(600, 240))
            screen.blit(custom_image, custom_image_rect.topleft)
        else:
            draw_text(screen, 'Image not found', (600, 240), large_font)

        # 画面の更新
        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()

完成イメージは下図のようになります。

   

 

Hayate.Labに戻る

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA