Raspberry Piでバス会社のリアルタイムの運行情報をスクレイピングして、運行状況を電光表示板に表示します。朝の忙しいときに、ささっと遅延状況を確認できるので便利です。
使用機材
- Raspberry Pi 2 Model B
- OLEDモジュール: SO1602AWWB-UC-WB (16x2行 白色)
表示板は明るくてコントラスが高い有機EL表示器を使用しました。離れたところからもくっきりと見やすいです。秋月電子で購入しました。
配線図
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
さいごに
表示板に常時表示されているので、朝の身支度をしながらチラ見することができて、とても便利です。
次回はOLEDモジュールをもう一つ使って、現在の天気と予想気温を表示してみたいと思います。お出かけ前に服装を決めるのに便利そうかなと思います。