Raspberry Pi Picoを使って東横線の運行情報を表示するインテリアを作る

技術系

はじめまして、りょうです。

先日とある雑貨屋さんを訪れた際、NYのメトロの運行状況がリアルタイムで表示される基盤のようなインテリアを見つけました。

一目見て「これ欲しい!」と思わせるほど魅力的なアイテムでした。
ニューヨークの地下鉄の路線情報がリアルタイムで表示されるこのアイテムは、インテリアとしてもだいぶおしゃれで、まるで海外の都会的な雰囲気を部屋に取り入れたような気分にさせてくれます。

しかし、値札を見て驚愕です。35,000円。正直、手が届きません。普通に東京タイ往復航空代くらいします。
買うことは諦めましたが、その魅力が忘れられず、ふと思いました。「これ、自分で作れないかな?」と。

この記事では、そんな思いつきから始まった「東横線運行状況ディスプレイ」のDIYプロジェクトを紹介します。ハードウェアやソフトウェアを使った作り方や、実際に完成させるまでの工程を解説していきます。

「好きな路線をインテリアにできたら楽しそう!」と思う方は、ぜひ最後までお付き合いください。

スポンサーリンク

購入品紹介

今回のプロジェクトでは、比較的手に入りやすい電子パーツを使います。
必要な材料と、それぞれの費用目安は以下の通りです。すべて揃えても約2,500円程度で収まります。

32,500円浮いた!やった!

1. Raspberry Pi Pico

価格: 約1,400円
Raspberry Pi Picoは、小型で扱いやすいマイコンボードです。今回のディスプレイでは、LEDの制御や東横線の運行情報を取得するプログラムを動かすための頭脳となる部分です。

2. LED (複数個)

価格: 300円
ディスプレイの主要部分です。運行状況に応じて点灯させたり、色を変えることで視覚的に状況を伝えます。赤カラーLEDを使うことで、東横線っぽくできます。

3. ブレッドボード

価格: 300円
ブレッドボードは、回路を組む際にハンダ付け不要で接続できる便利なアイテムです。今回のプロジェクトでは、LEDとRaspberry Pi Picoを接続するために使用します。

はんだ付けはさぼりました。

4. ジャンプワイヤ (ジャンパーケーブル)

価格: 300円
ブレッドボードとRaspberry Pi Picoを接続するためのケーブルです。オス-オス、オス-メスなど、用途に合わせたタイプを用意してください。

今回はオス-オスのみ使います。

5. その他 (オプション)

  • 電源ケーブル: 約200円
    Raspberry Pi Picoに電力を供給するためのUSBケーブル。タイプBを使用します。

実際に作っていく

このセクションでは、東横線の運行状況をディスプレイに反映するプログラムの概要を説明します。
今回は、駅探の平日時刻表を活用してスクレイピングし、保存したデータをもとに各駅の発車時刻に対応するLEDを点灯させます。

運行状況のロジック

  1. 時刻表データの取得
    駅探のウェブサイトから、平日の発車時刻表をスクレイピングしてデータを取得します。このデータをRaspberry Pi Picoに保存して利用します。
  2. LED制御の基本動作
    各駅をRaspberry Pi PicoのGPIOピンに対応させ、該当の発車時刻になった場合にLEDを点灯させます。

接続構成

GPIOピンを東横線の駅に対応させて接続します。以下の対応関係を基にLEDを配置してください。

  • GPIOピン: 1〜21番
  • 駅対応: 渋谷(TY1)〜元町・中華街(TY21)

例:

  • GPIOピン1 → 渋谷(TY1)
  • GPIOピン2 → 代官山(TY2)
  • ・・・
  • GPIOピン21 → 元町・中華街(TY21)

こんな感じになりました。
向かって左が横浜、右が渋谷の配置です。

思ってたんとちゃうな。。。と思った方、もうちょとお付き合いください。

プログラムの流れ

以下は、プログラムの主な流れです。

  1. WiFi接続
    Raspberry Pi PicoをWiFiに接続します。この通信により、現在の正確な時刻を取得します。
  2. 現在時刻の取得
    NTP(Network Time Protocol)を使用して現在時刻を取得します。
  3. 時刻表の確認
    保存してある時刻表データ(配列形式)と現在時刻を比較します。
  4. LEDのON/OFF制御
    • 現在時刻が時刻表の配列に含まれている場合、その駅に対応するGPIOピンをHIGHにしてLEDを点灯。
    • 含まれていない場合、GPIOピンをLOWにしてLEDを消灯。

NTPへは最初の1回のみ問い合わせ、その後はRaspberry Pi Pico内蔵の時計を参照します。
こんな流れで3,4を10秒おきに実行します

ソースコード

実際に動かすメインのプログラム

import ntptime
import train
import time
import network
import machine
from machine import RTC

#LED
led = machine.Pin("LED", machine.Pin.OUT)

#eki_led
eki = [machine.Pin(1, machine.Pin.OUT),
       machine.Pin(2, machine.Pin.OUT),
       machine.Pin(3, machine.Pin.OUT),
       machine.Pin(4, machine.Pin.OUT),
       machine.Pin(5, machine.Pin.OUT),
       machine.Pin(6, machine.Pin.OUT),
       machine.Pin(7, machine.Pin.OUT),
       machine.Pin(8, machine.Pin.OUT),
       machine.Pin(9, machine.Pin.OUT),
       machine.Pin(10, machine.Pin.OUT),
       machine.Pin(11, machine.Pin.OUT),
       machine.Pin(12, machine.Pin.OUT),
       machine.Pin(13, machine.Pin.OUT),
       machine.Pin(14, machine.Pin.OUT),
       machine.Pin(15, machine.Pin.OUT),
       machine.Pin(16, machine.Pin.OUT),
       machine.Pin(17, machine.Pin.OUT),
       machine.Pin(18, machine.Pin.OUT),
       machine.Pin(19, machine.Pin.OUT),
       machine.Pin(20, machine.Pin.OUT),
       machine.Pin(21, machine.Pin.OUT)]

wday = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']

def zfill(s, width):
    if len(s) < width:
        return ("0" * (width - len(s))) + s
    else:
        return s

def get_now():
    t=0
    while t==0:
        t1=ntptime.time()
        t = t1 + 9*60*60
    print(t)
    datetimetuple = time.gmtime(t)
    year = str(datetimetuple[0])
    month = zfill(str(datetimetuple[1]),2)
    mday = zfill(str(datetimetuple[2]),2)
    hour = zfill(str(datetimetuple[3]),2)
    minute = zfill(str(datetimetuple[4]),2)
    seconds = zfill(str(datetimetuple[5]),2)
    wd_index = datetimetuple[6]

    datetime = [year, month, mday, hour, minute, seconds, wd_index, 0]
    return datetime

led.value(1)

ssid = '<Wi-Fi SSID>'
password = '<Wi-Fi Password>'
 
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
 
max_wait = 10
while max_wait > 0:
    if wlan.status() < 0 or wlan.status() >= 3:
        break
    max_wait = max_wait - 1
    print('waiting for connection...')
    time.sleep(1)
    
if wlan.status() != 3:
    print(wlan.staus())
    raise RuntimeError('network connection failed')
else:
    print('connected')
    status = wlan.ifconfig()
    print('ip = ' + status[0])
    
led.value(0)

# RTC 初期化(Raspberry Pi Pico の内蔵 RTC を使用)
rtc = RTC()

now = get_now()
rtc.datetime((
    int(now[0]),  # 年
    int(now[1]),  # 月
    int(now[2]),  # 日
    int(now[6]),  # 曜日
    int(now[3]),  # 時
    int(now[4]),  # 分
    int(now[5]),  # 秒
    int(now[7])   # サブ秒
))

while True:
    # 現在時刻を取得
    current_time = rtc.datetime()

    # 時刻の整形
    formatted_time = "{:02d}:{:02d}".format(
        current_time[4], current_time[5]
    )

    # 時刻を表示
    print("現在時刻:", formatted_time)

    results = train.train(formatted_time)

    # 結果を出力
    if results:
        print(results)
        i = 0
        for r in results:
            if r == 1:
                eki[i].value(1)
            else:
                eki[i].value(0)
            i += 1
    else:
        print(f"現在時刻 {formatted_time} は配列のどこにもありません。")

    # 10秒待機
    time.sleep(10)

NTPサーバに時刻を問い合わせるプログラム

import usocket as socket
try:
    import ustruct as struct
except:
    import struct
    
    
def time():
    NTP_DELTA = 2208988800
    # ntp server
    #ntp_host = "ntp.nict.jp"
    #ntp_host = "time-c.nist.gov"
    #ntp_host = "time.cloudflare.com"
    ntp_host = "time.google.com"
    
    NTP_QUERY = bytearray(48)
    NTP_QUERY[0] = 0x1b
    addr = socket.getaddrinfo(ntp_host, 123)[0][-1]
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # s.settimeout(1)
    res = s.sendto(NTP_QUERY, addr)
    msg = s.recv(48)
    s.close()
    val = struct.unpack("!I", msg[40:44])[0]
    return val - NTP_DELTA

時刻表を保持して現在が発車時刻か判定するプログラム

def train(formatted_time):
    jikoku=[['05:00', '05:16', '05:23', '05:29', '05:42',...
             (21駅分の時刻表の配列)
             ... ]]
        # 配列内に現在時刻があるかを確認
    results = []
    for idx, sublist in enumerate(jikoku):
        if formatted_time in sublist:
            results.append(1)
        else:
            results.append(0)
    return results

突貫工事で作成したので、いろんなプログラムのキメラみたいになりました。
読みにくかったらすみません。

実際に動かす

ちゃんと光った。

向かって左側が渋谷駅で、右側が横浜駅を表示しています。

普段使っている路線がリアルタイムで視覚的に表示されるだけでなく、自分で作ったという達成感が、さらに愛着を深めてくれます。

ブレッドボードむき出しなので部屋の電気を消してみます。

ちょっと怖いですね。
ハッカーの部屋にありがちなインテリアになりました。

インテリアとしての魅力

特に注目したいのは、その独特な「時限爆弾っぽい」見た目です。
思ってたのと違います。
おしゃれな見た目にするためには、今のところあと3万円上乗せするしかないです。

課題とこれからの可能性

一方で、「時限爆弾っぽい」というデザインは人を選ぶかもしれません。
そのため、LEDの色やケースデザインを変更して、さらに洗練された見た目にカスタマイズする余地もあります。

またせっかくWi-Fiと接続しているのに、保持してある時刻表と比べるのみで、実際の現状の遅れなどの運行状況は反映できていません。
拡張の可能性は広がりますが、有効なAPIなどが提供されていなかったので、またの機会に実装は考えます。

最後に

今回、自作した「東横線の運行状況を表示するインテリア」は、実用性と遊び心を兼ね備えたアイテムとして、大満足の仕上がりになりました。
日常で利用する路線を自分でプログラミングして再現することで、普段何気なく使っている鉄道がより身近に感じられるようになった気がします。

また、初めてRaspberry Pi Picoを触ったので、いい経験になりました!

スポンサーリンク
タイトルとURLをコピーしました