はじめに
本記事はESP8266を使って天気予報読み上げスピーカーが動くまでの過程を記録したものです。興味本位にmicropythonを使ったことで無用な苦労をしたので、同じ轍を踏まないように記録を公開します。
最終版
100均ライトからケース、スイッチ、電池ボックスを流用してスピーカーとユニバーサル基板を格納しました。
試作版は100均ライトに格納不能だったのでスピーカーも基板も試作版とは別ものを格納しています。
準備する
前々回の記事 ESP8266でmicropythonを使う を参照ください。
天気予報を取得する
urequestsを使って天気予報を取得します。
yahooの天気予報(RSS)を使うつもりでしたがhttpsのため情報が取得できません。
httpsアクセス
>>> import urequests
>>> r = urequests.get('https://rss-weather.yahoo.co.jp/rss/days/4410.xml')
TLS buffer overflow, record size: 6073 (+5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "aa.py", line 3, in <module>
File "urequests.py", line 116, in get
File "urequests.py", line 62, in request
OSError: (-257, 'RECORD_OVERFLOW')
httpで取得できるgooの天気予報(RSS)を使います。
httpアクセス
import urequests
# 東京
r = urequests.get('http://weather.goo.ne.jp/area/4410.rdf')
lines = str(r.content)
line = lines.split("\\n")
description = line[10]
description = description.replace("<description>","")
description = description.replace("</description>","")
print(description)
暗号文EUC-JPで「晴れ 時々 曇り」という文字列が取得できました。
\xc0\xb2\xa4\xec \xbb\xfe\xa1\xb9 \xc6\xde\xa4\xea
encodeでECU-JPからUTF-8に変換するのですがmicropythonではうまくいきません。
b'\xc0\xb2\xa4\xec \xbb\xfe\xa1\xb9 \xc6\xde\xa4\xea'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "rss_test.py", line 39, in test
File "rss_test.py", line 88, in get_tenki
UnicodeError:
ググっても原因が分からなかったので追試してみます。
pythonとmicropythonで動作を比較して原因を突き止めます。
まずはwindows上のpythonで下記のコードを実行します。
b_utf = "晴れ 時々 曇り".encode("UTF-8")
print(b_utf)
b_euc = "晴れ 時々 曇り".encode("EUC-JP")
print(b_euc)
str = b_euc.decode("EUC-JP")
print(str)
実行結果
b'\xe6\x99\xb4\xe3\x82\x8c \xe6\x99\x82\xe3\x80\x85 \xe6\x9b\x87\xe3\x82\x8a'
b'\xc0\xb2\xa4\xec \xbb\xfe\xa1\xb9 \xc6\xde\xa4\xea'
晴れ 時々 曇り
micropython で実行します。
b_utf = "晴れ 時々 曇り".encode("UTF-8")
print(b_utf)
b_euc = "晴れ 時々 曇り".encode("EUC-JP")
print(b_euc)
str = b_euc.decode("EUC-JP")
print(str)
実行結果
b'\xe6\x99\xb4\xe3\x82\x8c \xe6\x99\x82\xe3\x80\x85 \xe6\x9b\x87\xe3\x82\x8a'
b'\xe6\x99\xb4\xe3\x82\x8c \xe6\x99\x82\xe3\x80\x85 \xe6\x9b\x87\xe3\x82\x8a'
晴れ 時々 曇り
>>>
ECU-JPとUTF-8が同じbytesに変換されています。
micropythonはUTF-8のみ実装されていると思われます。
真面目にEUC-JPからUTF-8に変換するのは難しそうなので、単語テーブル(ECU-JP)を使うことにします。
4. ソフトを作る
材料はそろったので、実験結果を元に、天気予報を読み上げるプログラムを作りました。
天気予報の読み上げだけでは寂しいので時間の読み上げ機能も付加しました
100均ライトのケースを使用し、電源スイッチが入ったら時間と天気予報を読み上げます。
単語テーブルには直近の天気(晴れ、雨、曇り)しか入れてないので、後日テーブルに不足した天気を追加します。
(後日下記ソースを修正する)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urequests
from dfplayermini import Player
from time import sleep
from machine import Pin
import ntptime
import time
HARE = "\\xc0\\xb2\\xa4\\xec"
KUMORI = "\\xc6\\xde\\xa4\\xea"
AME = "\\xb1\\xab"
TOKIDOKI = "\\xbb\\xfe\\xa1\\xb9"
NOCHI = "\\xa4\\xce\\xa4\\xc1"
ICHIJI = "\\xb0\\xec\\xbb\\xfe"
weather_view = ["一時", "後", "時々", "雨", "曇り", "晴れ"]
weather_table = [ICHIJI, NOCHI, TOKIDOKI, AME, KUMORI, HARE]
url = "http://weather.goo.ne.jp/area/4410.rdf"
# 時間を読み上げる
def talk_time(music):
ntptime.settime()
t = time.gmtime()
h = (t[3] + 9) % 24 # GMT+9
m = t[4]
print("只今の時間", end="")
music.play(8)
sleep(2)
print(" {0}時".format(h), end="")
music.play(h + 9)
sleep(1)
print(" {0}分".format(m))
music.play(m + 33)
sleep(2)
# 天気予報を読み上げる
def talk_tenki(music):
global weather_table, weather_view, url
print("明日の天気は", end="")
music.play(1)
sleep(1.2)
# 東京
r = urequests.get(url)
lines = str(r.content)
line = lines.split("\\n")
description = line[10]
description = description.replace("<description>", "")
description = description.replace("</description>", "")
flg = True
while flg:
flg = False
for i in range(len(weather_table)):
if description.startswith(weather_table[i]):
description = description.replace(weather_table[i], "")
description = description.strip()
print(" " + weather_view[i], end="")
music.play(2 + i)
sleep(0.7)
flg = True
break
print("です。")
music.play(2)
sleep(2)
# メイン処理
if __name__ == '__main__':
music = Player(1, 2)
# music.module_wake()
music.volume(20)
while True:
talk_time(music) # 時間を読み上げる
talk_tenki(music) # 天気予報を読み上げる
sleep(10)
5. 音声ファイルを用意する
私は音声ファイル作成にVoiceText Web APIを利用しています。
ただし、こちらのサービスは 「無料版で作成した音声データの商用利用、二次利用及び配布する行為は禁止されております。」とのことで完成版の動画公開はできません。商用利用や動画投稿サイトへの公開を許可している合成音声サービスもありますが、VoiceTextの自然な音声合成が非常に好みなのでなかなか他のサービスに移行できません。
ちょっと脱線しましましたが、下記のような音声ファイルを用意します。(この作業が一番面倒です)
DFPlayer miniの再生はトラックID指定なので
SDをフォーマットして空にした後、バッチファイルで音声ファイルをコピーすると意図した順序で音声ファイルを配置できます。
copy 明日の天気は.wav e:\
copy です.wav e:\
copy 一時.wav e:\
copy のち.wav e:\
copy 雨.wav e:\
copy 曇り.wav e:\
copy 晴れ.wav e:\
copy 只今の時間.wav e:\
copy 0時.wav e:\
copy 1時.wav e:\
copy 2時.wav e:\
copy 3時.wav e:\
copy 4時.wav e:\
copy 5時.wav e:\
copy 6時.wav e:\
copy 7時.wav e:\
copy 8時.wav e:\
copy 9時.wav e:\
copy 10時.wav e:\
copy 11時.wav e:\
copy 12時.wav e:\
copy 13時.wav e:\
copy 14時.wav e:\
copy 15時.wav e:\
copy 16時.wav e:\
copy 17時.wav e:\
copy 18時.wav e:\
copy 19時.wav e:\
copy 20時.wav e:\
copy 21時.wav e:\
copy 22時.wav e:\
copy 23時.wav e:\
copy 0分.wav e:\
copy 1分.wav e:\
copy 2分.wav e:\
copy 3分.wav e:\
copy 4分.wav e:\
copy 5分.wav e:\
copy 6分.wav e:\
copy 7分.wav e:\
copy 8分.wav e:\
copy 9分.wav e:\
copy 10分.wav e:\
copy 11分.wav e:\
copy 12分.wav e:\
copy 13分.wav e:\
copy 14分.wav e:\
copy 15分.wav e:\
copy 16分.wav e:\
copy 17分.wav e:\
copy 18分.wav e:\
copy 19分.wav e:\
copy 20分.wav e:\
copy 21分.wav e:\
copy 22分.wav e:\
copy 23分.wav e:\
copy 24分.wav e:\
copy 25分.wav e:\
copy 26分.wav e:\
copy 27分.wav e:\
copy 28分.wav e:\
copy 29分.wav e:\
copy 30分.wav e:\
copy 31分.wav e:\
copy 32分.wav e:\
copy 33分.wav e:\
copy 34分.wav e:\
copy 35分.wav e:\
copy 36分.wav e:\
copy 37分.wav e:\
copy 38分.wav e:\
copy 39分.wav e:\
copy 40分.wav e:\
copy 41分.wav e:\
copy 42分.wav e:\
copy 43分.wav e:\
copy 44分.wav e:\
copy 45分.wav e:\
copy 46分.wav e:\
copy 47分.wav e:\
copy 48分.wav e:\
copy 49分.wav e:\
copy 50分.wav e:\
copy 51分.wav e:\
copy 52分.wav e:\
copy 53分.wav e:\
copy 54分.wav e:\
copy 55分.wav e:\
copy 56分.wav e:\
copy 57分.wav e:\
copy 58分.wav e:\
copy 59分.wav e:\
6. 組付ける
6.1. main.pyを転送します。
- WebREPLのSend a File機能を使ってmain.pyを転送します。
6.2. ESP8266を再起動します
- WebREPLでCtrl+D(ソフトリセット) 、もしくはESP8266の電源を入れ直して再起動します。
6.3. main.pyを実行します。
- ESP8266起動時に自動的にmain.pyが実行されます。電源を入れると時間と天気予報を繰り返し読み上げてくれます。
6.4. これで天気予報読み上げスピーカーは完成です!
実用性は微妙ですが、なんとか完成しました。
あとは、RSSの取得でサーバーに負荷をかけないよう、天気予報をキャッシュする。
httpサーバー機能を付けて外部から設定できるようにして、ディープスリープモードを使って目覚まし機能や時報、アラーム機能などを付けたりしてみたいです。
今回、AmazonでDFPlayer miniを4つ999円で購入したので、まだ3つも余っています。
昔買ったのが押し入れから出てきて、実際には4つも余っている。(DIYのあるある・・ですよね!??)
次は何を作ろうかなぁ?
おまけ
ESP8266 Flash1M版は標準でunrequestがインストールされていないようです。
upipでインストールできました。
upipについてはmicropythonの公式サイトのこのあたりに記述があります。
MicroPython v1.17 on 2021-09-02; ESP module (1M) with ESP8266
Type "help()" for more information.
>>>
>>>
>>>
>>> import upip
>>> upip.install('urequests')
Installing to: /lib/
Warning: micropython.org SSL certificate is not validated
Installing urequests 0.6 from https://micropython.org/pi/urequests/urequests-0.6.tar.gz
>>> import urequests
>>>
micropythonもminidomが使えます。
ただしUTF-8でないとパースできないです。
windows上のpythonだとこんな感じで簡単なのですが・・・
import requests
from xml.dom import minidom
url = "http://weather.goo.ne.jp/area/4410.rdf"
r = requests.get(url)
rss_euc = r.content
rss = rss_euc.decode("EUC-JP")
dom = minidom.parseString(rss)
elems = dom.getElementsByTagName('description')
print(elems[0].childNodes[0].data)
晴れ 時々 曇り