IoTで使うPython入門Step1-I2C LM75Bで温度測定 (6) Web

OLEディスプレイの表示に漢字を使う

 前回、LM75Bで温度のデータを取得し、OLEDディスプレイに温度の表示ができました。漢字が表示できるように改良します。
 モジュールにしたoledModule.pyの最後の行(45行)、

font = ImageFont.truetype('FreeMono.ttf', 16)

 左辺のfontをfontANKに変更します。もう一つ、漢字の表示できるフォントを追加します。こちらはfontとしました。別途日本語フォントをインストールして使ってもかまいません。そのほうが、半角と全角を混在して表示ができます(たとえば、sudo apt-get install fonts-ipafontでインストールされるfonts-japanese-gothic.ttf)。下記のフォントは、ラズパイに入っているフォントです。

font = ImageFont.truetype('DroidSansFallbackFull.ttf', 16)

 以上のように、oledModule.pyの変更は2行だけです。

fontANK = ImageFont.truetype('FreeMono.ttf', 16)
font = ImageFont.truetype('DroidSansFallbackFull.ttf', 16)

 つぎに、lm75.pyの先頭に、

#coding:utf-8

を追加します。ソース・コードの中の漢字が正常に表示できるようになります。
 実際にOLEDの表示に漢字を使うときは、u"温度"のようにuを前につけます。漢字のフォントが大きいので、温度を表示する2行目の位置をtop+16からtop+18に、漢字を表示する部分はfont、アルファベットを表示する部分はfontANKへ変更します。プログラムです。

#coding:utf-8
import smbus
import time
import oledModule as OLED

i2c = smbus.SMBus(1)
addr = 0x4c # LM75B NXP. TI is 9bit
Tos = 0x03 # 9bit default 0x5000 80

def sign16(x):
return (-(x & 0b1000000000000000) | (x & 0b0111111111111111))

# main
readTos = i2c.read_i2c_block_data(addr, Tos, 2)
print("Tos ", readTos)
i2c.write_byte_data(addr, Tos, 0x1a) # set 26.0C
OLED.disp.clear()
while 1:
OLED.draw.rectangle((0,0,OLED.width,OLED.height), outline=0, fill=0) # Draw a black filled box to clear the image.
data = i2c.read_i2c_block_data(addr, 0, 2)
raw = data[0] * 256 + data[1]
raw_s = sign16(raw)
temp = (raw_s >> 5) * 0.125
print(temp, "C", readTos[0])
if temp >= readTos[0] :
OLED.draw.text((0, OLED.top), "Temp: #over", font=OLED.fontANK, fill=255)
OLED.draw.text((0, OLED.top), u"温度", font=OLED.font, fill=255)
OLED.draw.text((0, OLED.top+18), str(temp) + "C", font=OLED.fontANK, fill=255)
OLED.disp.image(OLED.image) # Display image.
OLED.disp.display()
time.sleep(3)

 実行している様子です。

Webに温度データを表示

 Pythonには、テスト用にWebサーバをローカルで動かす仕組みがあります。外部にレンタル・サーバを借りなくてもテストできます。ローカルなので、セキュリティを気にする必要もなく、必要なときに動けばよいという使い方です。LANでつかながった別のPCからアクセスできます。

Webサーバの起動

 ターミナルでpythonと打つと、2.7が動きます。Webサーバの起動は次のようにシンプルです。

python -m SimpleHTTPServer

 Python3だと、コマンドが異なります。

python3 -m http.server 8000

 lm75.pyにindex.htmlの書き出しを追加します。
 Webクライアント(ブラウザ)はデフォルトでindex.htmlもしくはマイクロソフトのWebサーバであればdefault.htmとかをアクセスします。上記のサーバが起動すると、サーバを起動したディレクトリと同じところにindex.htmlができます。その中身を書き換えます(31~33行)。
 整理すると、いま、piのホーム・ディレクトリにいて、そこには、lm75.py、oledModule.py、index.htmlがあります。

#coding:utf-8
import smbus
import time
import oledModule as OLED

i2c = smbus.SMBus(1)
addr = 0x4c # LM75B NXP. TI is 9bit
Tos = 0x03 # 9bit default 0x5000 80

def sign16(x):
return (-(x & 0b1000000000000000) | (x & 0b0111111111111111))

# main
readTos = i2c.read_i2c_block_data(addr, Tos, 2)
print("Tos ", readTos)
i2c.write_byte_data(addr, Tos, 0x1a) # set 26.0C
OLED.disp.clear()
while 1:
OLED.draw.rectangle((0,0,OLED.width,OLED.height), outline=0, fill=0) # Draw a black filled box to clear the image.
data = i2c.read_i2c_block_data(addr, 0, 2)
raw = data[0] * 256 + data[1]
raw_s = sign16(raw)
temp = (raw_s >> 5) * 0.125
print(temp, "C", readTos[0])
if temp >= readTos[0] :
OLED.draw.text((0, OLED.top), "Temp: #over", font=OLED.fontANK, fill=255)
OLED.draw.text((0, OLED.top), u"温度", font=OLED.font, fill=255)
OLED.draw.text((0, OLED.top+18), str(temp) + "C", font=OLED.fontANK, fill=255)
OLED.disp.image(OLED.image) # Display image.
OLED.disp.display()
f = open('index.html','w')
f.write("Temp:" + str(temp) + "C ")
f.close()
time.sleep(3)

クライアント側の操作

 別のPCもしくはラズパイのWebブラウザからアクセスします。通常のポート80ではないので、明示的に8000を入れてアクセスします。URLに、

 <ラズパイのIPアドレス>:8000

 ラズパイのIPアドレスは、

hostname -I

で表示できます。
 サーバを立ち上げたターミナルには、クライアントからのアクセスが表示されます。

 lm75.pyを動かします。

 Webブラウザに表示が出ます。待っていても温度の値は更新されません。リロード・ボタンを押すと新しい値になります。

 これでは不便なので、温度を表示する、

f.write("Temp:" + str(temp) + "C ")

の前の行に読み込みをリフレッシュする命令を追加します。

f.write('<META HTTP-EQUIV="Refresh" CONTENT="1">')

 Webブラウザを見ているだけで、最新の温度が1秒ごとに更新されるようになります。
 文字が小さいので、温度の値を表示する部分を修正します。

f.write('<span style="font-size:60px;">'"Temp: " + str(temp) + "C "'</span>')

 表示です。見やすくなりました。

Webの表示を漢字対応に

 index.htmlをアクセスしたら、最初にクライアントのWebブラウザに文字の種類を送るように次の1行を追加します。日常見ているコンテンツにもこのMETAタグは記述されています。

f.write('<META http-equiv="Content-Type" content="text/html; charset=UTF-8">')

 「Temp: 」の代わりに漢字の「温度: 」を入れます。

f.write('<span style="font-size:60px;">'"温度: " + str(temp) + "C "'</span>')

 実際の表示です。

ターミナルの表示も漢字を使う

 OLEDには u"温度: " Webには "温度: " です。同じutf-8を使っています。ターミナルで「温度」の漢字を表示するには、python2なので、かっこを取ります。

print "温度: ", temp, "C", readTos[0]

 このように、ターミナル、OLED、Webでいろいろ表現方法があるという困った状況です。調べると、ターミナルは、ユニコードをstrと勝手に解釈して出力するそうです。

 プログラムでは先頭に#coding:utf-8と書いたので、ソース・コード中にあるアスキ文字でないエンコード形式はユニコードだとわかります(Python3ではデフォルトのcodingがutf-8)が、これは文字化けしないだけの効果しかありません。

 Webへは、HTML文の最初にcharset=UTF-8を送ったので、漢字の「温度: 」が送られてくるとユニコードだと解釈してくれます。

 OLEDドライバはユニコードをを要求するようです。OLEDへはユニコードだと明示するためにu"温度 "と送って文字化けはなくなりました。

 最終のlm75.pyです。

#coding:utf-8
import smbus
import time
import oledModule as OLED
i2c = smbus.SMBus(1)
addr = 0x4c # LM75B NXP. TI is 9bit
Tos = 0x03 # 9bit default 0x5000 80

def sign16(x):
return (-(x & 0b1000000000000000) | (x & 0b0111111111111111))

# main
readTos = i2c.read_i2c_block_data(addr, Tos, 2)
print("Tos ", readTos)
i2c.write_byte_data(addr, Tos, 0x1a) # set 26.0C
OLED.disp.clear()
while 1:
OLED.draw.rectangle((0,0,OLED.width,OLED.height), outline=0, fill=0) # Draw a black filled box to clear the image.
data = i2c.read_i2c_block_data(addr, 0, 2)
raw = data[0] * 256 + data[1]
raw_s = sign16(raw)
temp = (raw_s >> 5) * 0.125
print "温度:",temp, "C", readTos[0]
if temp >= readTos[0]:
OLED.draw.rectangle((0,0,OLED.width,OLED.height), outline=0, fill=0)
OLED.draw.text((0, OLED.top), "Temp: #over", font=OLED.fontANK, fill=255)
else:
OLED.draw.text((0, OLED.top), u"温度", font=OLED.font, fill=255)
OLED.draw.text((0, OLED.top+18), str(temp) + u"C", font=OLED.fontANK, fill=255)
OLED.disp.image(OLED.image) # Display image.
OLED.disp.display()
f = open('index.html','w')
f.write('<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">')
f.write('<META HTTP-EQUIV="Refresh" CONTENT="1">')
f.write('<span style="font-size:60px;">'"温度" + str(temp) + "℃"'</span>')
f.close()
time.sleep(3)