LoginSignup
3
3

More than 5 years have passed since last update.

ラズパイでバスの接近情報を電光表示板に表示する

Last updated at Posted at 2019-04-20

IMG_3090 (3).jpg

Raspberry Piでバス会社のリアルタイムの運行情報をスクレイピングして、運行状況を電光表示板に表示します。朝の忙しいときに、ささっと遅延状況を確認できるので便利です。

使用機材

  • Raspberry Pi 2 Model B
  • OLEDモジュール: SO1602AWWB-UC-WB (16x2行 白色)

表示板は明るくてコントラスが高い有機EL表示器を使用しました。離れたところからもくっきりと見やすいです。秋月電子で購入しました。

配線図

image.png

Raspberry Pi SO1602
1(3.3V) 2(VDD), 4(SA0)
3(SDA) 8(SDAin), 9(SDAout)
5(SCL) 7(SLC)
6(GND) 1(VSS), 3(/CS)

OLEDモジュールのPythonライブラリー

OLEDモジュール(SO1602)用のPython用のライブラリーはこちらを使わせていただきました。ただし、たまにwrite時にI/O Errorが発生することがあり、write前後のsleepの値をいろいろ変更しても回避できなかっため、try-exceptでリトライするようにしました。もしかすると、作成中の環境では電源容量が足りていなかったせいかもしれません。現在は安定しています。

so1602.py
# -*- coding: utf-8 -*-
import unicodedata
import smbus
import time
import sys

i2c = smbus.SMBus(1)

CHAR_TABLE = {
    u'†': [0x11],
    u'§': [0x12],
    u'¶': [0x13],
    u'Γ': [0x14],
    u'Δ': [0x15],
    u'θ': [0x16],
    u'Λ': [0x17],
    u'Ξ': [0x18],
    u'Π': [0x19],
    u'Σ': [0x1a],
    u'Φ': [0x1c],
    u'Ψ': [0x1d],
    u'Ω': [0x1e],
    u'α': [0x1f],

    u' ': [0x20],
    u'!': [0x21],
    u'"': [0x22],
    u'#': [0x23],
    u'$': [0x24],
    u'%': [0x25],
    u'&': [0x26],
    u"'": [0x27],
    u'(': [0x28],
    u')': [0x29],
    u'*': [0x2a],
    u'+': [0x2b],
    u',': [0x2c],
    u'-': [0x2d],
    u'.': [0x2e],
    u'/': [0x2f],

    u'0': [0x30],
    u'1': [0x31],
    u'2': [0x32],
    u'3': [0x33],
    u'4': [0x34],
    u'5': [0x35],
    u'6': [0x36],
    u'7': [0x37],
    u'8': [0x38],
    u'9': [0x39],
    u':': [0x3a],
    u';': [0x3b],
    u'<': [0x3c],
    u'=': [0x3d],
    u'>': [0x3e],
    u'?': [0x3f],

    u'@': [0x40],
    u'A': [0x41],
    u'B': [0x42],
    u'C': [0x43],
    u'D': [0x44],
    u'E': [0x45],
    u'F': [0x46],
    u'G': [0x47],
    u'H': [0x48],
    u'I': [0x49],
    u'J': [0x4a],
    u'K': [0x4b],
    u'L': [0x4c],
    u'M': [0x4d],
    u'N': [0x4e],
    u'O': [0x4f],

    u'P': [0x50],
    u'Q': [0x51],
    u'R': [0x52],
    u'S': [0x53],
    u'T': [0x54],
    u'U': [0x55],
    u'V': [0x56],
    u'U': [0x55],
    u'V': [0x56],
    u'W': [0x57],
    u'X': [0x58],
    u'Y': [0x59],
    u'Z': [0x5a],
    u'[': [0x5b],
    u'¥': [0x5c],
    u']': [0x5d],
    u'^': [0x5e],
    u'_': [0x5f],

    u'`': [0x60],
    u'a': [0x61],
    u'b': [0x62],
    u'c': [0x63],
    u'd': [0x64],
    u'e': [0x65],
    u'f': [0x66],
    u'g': [0x67],
    u'h': [0x68],
    u'i': [0x69],
    u'j': [0x6a],
    u'k': [0x6b],
    u'l': [0x6c],
    u'm': [0x6d],
    u'n': [0x6e],
    u'o': [0x6f],

    u'p': [0x70],
    u'q': [0x71],
    u'r': [0x72],
    u's': [0x73],
    u't': [0x74],
    u'u': [0x75],
    u'v': [0x76],
    u'w': [0x77],
    u'x': [0x78],
    u'y': [0x79],
    u'z': [0x7a],
    u'{': [0x7b],
    u'|': [0x7c],
    u'}': [0x7d],
    u'→': [0x7e],
    u'←': [0x7f],

    u'。': [0xa1],
    u'「': [0xa2],
    u'」': [0xa3],
    u'、': [0xa4],
    u'・': [0xa5],
    u'ヲ': [0xa6],
    u"ァ": [0xa7],
    u'ィ': [0xa8],
    u'ゥ': [0xa9],
    u'ェ': [0xaa],
    u'ォ': [0xab],
    u'ャ': [0xac],
    u'ュ': [0xad],
    u'ョ': [0xae],
    u'ッ': [0xaf],

    u'ー': [0xb0],
    u'ア': [0xb1],
    u'イ': [0xb2],
    u'ウ': [0xb3],
    u'エ': [0xb4],
    u'オ': [0xb5],
    u'カ': [0xb6],
    u'キ': [0xb7],
    u'ク': [0xb8],
    u'ケ': [0xb9],
    u'コ': [0xba],
    u'サ': [0xbb],
    u'シ': [0xbc],
    u'ス': [0xbd],
    u'セ': [0xbe],
    u'ソ': [0xbf],

    u'タ': [0xc0],
    u'チ': [0xc1],
    u'ツ': [0xc2],
    u'チ': [0xc1],
    u'ツ': [0xc2],
    u'テ': [0xc3],
    u'ト': [0xc4],
    u'ナ': [0xc5],
    u'ニ': [0xc6],
    u'ヌ': [0xc7],
    u'ネ': [0xc8],
    u'ノ': [0xc9],
    u'ハ': [0xca],
    u'ヒ': [0xcb],
    u'フ': [0xcc],
    u'ヘ': [0xcd],
    u'ホ': [0xce],
    u'マ': [0xcf],

    u'ミ': [0xd0],
    u'ム': [0xd1],
    u'メ': [0xd2],
    u'モ': [0xd3],
    u'ヤ': [0xd4],
    u'ユ': [0xd5],
    u'ヨ': [0xd6],
    u'ラ': [0xd7],
    u'リ': [0xd8],
    u'ル': [0xd9],
    u'レ': [0xda],
    u'ロ': [0xdb],
    u'ワ': [0xdc],
    u'ン': [0xdd],
    u'゛': [0xde],
    u'゜': [0xdf],

    u''':[0xf0],
    u'"':[0xf1],
    u'°':[0xf2],
    u'×': [0xf7],
    u'÷': [0xf8],
    u'≧': [0xf9],
    u'≦': [0xfa],
    u'≪': [0xfb],
    u'≫': [0xfc],
    u'≠': [0xfd],
    u'√': [0xfe],
    u' ̄': [0xff],

    u'ガ': [0xb6, 0xde],
    u'ギ': [0xb7, 0xde],
    u'グ': [0xb8, 0xde],
    u'ゲ': [0xb9, 0xde],
    u'ゴ': [0xba, 0xde],
    u'ザ': [0xbb, 0xde],
    u'ジ': [0xbc, 0xde],
    u'ズ': [0xbd, 0xde],
    u'ゼ': [0xbe, 0xde],
    u'ゾ': [0xbf, 0xde],
    u'ダ': [0xc0, 0xde],
    u'ヂ': [0xc1, 0xde],
    u'ヅ': [0xc2, 0xde],
    u'デ': [0xc3, 0xde],
    u'ド': [0xc4, 0xde],
    u'バ': [0xca, 0xde],
    u'ビ': [0xcb, 0xde],
    u'ブ': [0xcc, 0xde],
    u'ベ': [0xcd, 0xde],
    u'ボ': [0xce, 0xde],
    u'パ': [0xca, 0xdf],
    u'ピ': [0xcb, 0xdf],
    u'プ': [0xcc, 0xdf],
    u'ペ': [0xcd, 0xdf],
    u'ポ': [0xce, 0xdf],
}
def setaddr( address ):
        global addr
        addr = address

def command( code ):
        global addr
        count = 0
        while count < 10:
            try:
                i2c.write_byte_data(addr, 0x00, code)
                time.sleep(0.0002)
                break
            except Exception as e:
                print(e)
                count += 1

def write( message ):
    global addr
    if sys.version_info.major == 2 :
       message = message.decode('utf-8')
    mojilist = []
    for char in message:
        mojilist += CHAR_TABLE[char]

    count = 0
    while count < 10:
        try:
            i2c.write_i2c_block_data(addr, 0x40, mojilist)
            time.sleep(0.0002)
            break
        except Exception as e:
            print(e)
            count += 1

バスの運行情報をスクレイピング

神奈中バスのサイトの接近情報をスクレイピングします。OLEDモジュールの表示桁数が少ないため、遅延の有り無しで表示量を調整しています。最後にOLEDモジュールに表示内容を書き出したら、完成です。

bus.py
import urllib.request
import cchardet
import re
import so1602
from bs4 import BeautifulSoup

def getBusTime(url):
    with urllib.request.urlopen(url) as res:
        byte = res.read()
        html = byte.decode(cchardet.detect(byte)['encoding'])
        soup = BeautifulSoup(html, 'html.parser')

        #title = soup.head.title
        #print('[title]:', title.text, '\n')

        delay = ''
        for block in soup.find_all('div', {'class':{'frameBox03'}}):
            items = block.text.split()
            #print('[frameBox03]:', items)
            # 0: 'xxxにあとmm分で到着'
            # 1: '運行中'
            # 2: '(mm分遅れ)'
            if len(items) > 2:
                #print('[items]:', items[2])
                text = items[2] # '(mm分遅れ)'
                text = text.strip('() ()') # 括弧を除去

                pattern = r'[0-9]*'
                result = re.search(pattern, text)
                delay = result.group(0)
                #print('遅延:', delay)
            break

        arrivals = []
        for block in soup.find_all('div', {'class':{'placeArea01 departure'}}):
            #print('[placeArea01]:', block.text)
            items = block.text.split()
            text = items[1]  #'(hh:mm着予定)'
            text = text.strip('()') # 括弧を除去

            pattern = r'[0-9]{1,2}:[0-9]{1,2}'
            result = re.search(pattern, text)
            arrivals.append(result.group(0))
            #print('到着時間:', result.group(0))

            if len(delay) > 0:
                break

    return arrivals, delay

def getBusTimeLines(urls):
    buses = []
    for url in urls:
        results = getBusTime(url)
        arrivals = results[0]
        delay = results[1]
        count = 0
        text = ''
        for arrival in arrivals:
            if count > 0:
                text += ' '
            text += arrival
            if count == 0 and len(delay) != 0:
                text += '(-' + delay + ')'
            count += 1
            if count >= 2:
                break

        buses.append(text)

    return buses

def display(addr, line1, line2):
    so1602.setaddr(addr)

    # LCD初期化
    so1602.command(0x0c)
    so1602.command(0x01)

    # LCD表示
    # 1行目
    so1602.command(0x80)
    so1602.write(line1)

    # 2行目
    so1602.command(0xa0)
    so1602.write(line2)

if __name__ == '__main__':
    urls = [
        'http://real.kanachu.jp/pc/displayapproachinfo?fNO=11240&tNO=11223',
        'http://real.kanachu.jp/pc/displayapproachinfo?fNO=11240&tNO=11221'
    ]
    buses = getBusTimeLines(urls)

    line1 = 'H→K ' + buses[0]
    line2 = 'N→H ' + buses[1]
    print(line1)
    print(line2)
    display(0x3d, line1, line2)

表示例

$ python3 bus.py

うまく表示できたら、cronにセットします。

*/1 * * * * python3 /home/pi/programs/bus.py

image.png

さいごに

表示板に常時表示されているので、朝の身支度をしながらチラ見することができて、とても便利です。
次回はOLEDモジュールをもう一つ使って、現在の天気と予想気温を表示してみたいと思います。お出かけ前に服装を決めるのに便利そうかなと思います。

参考

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3