Itsukaraの日記

最新IT技術を勉強・実践中。最近はDeep Learningに注力。

「みんなのPython勉強会」で頂いたESP8266にハマってます

Pythonの勉強会があると知り、先週参加しました。この勉強会は、Pythonの初心者から熟練者まで、色々な人が集まる会とのこと。今回は、「Pythonデータサイエンス祭り」と題し、Python機械学習を活用するための進め方を中心に紹介されました。非常にタメになる内容でした。

みんなのPython勉強会

勉強会には100名以上の人が参加し、非常に活況でした。参加申し込みが定員の1.5倍程度あり、私は参加申し込みが遅かったために、当初、補欠扱いでした。参加できるか気を揉んでいましたが、結局、多数のキャンセルが出て、勉強会開催の5分前に参加できることが決まりました。

主催者側のご説明によると、3月に、Google人工知能AlphaGoが、韓国のイ・セドル氏(世界で一番強いと考えられている囲碁プレーヤー)に完全勝利したことをきっかけに、Pythonへの関心が高まっているとのこと。Python機械学習の主流プラットフォームになっていること、および、AlphaGoをきっかけに世界各国でAIに対して数兆円規模の研究開発投資が始まり、日本の各企業でも、AI基盤としてPythonへの関心が高まっているようです。

勉強会のセミナー終了後に開催された懇談会で参加者数名と話したところでは、企業各社で、Python機械学習の様子を見に来ているようでした。

MicroPython on ESP8266:頂きました

実は、懇親会で行われたLightning talkで、MicroPython on ESP8266の紹介がありました。ESP8266は、秋葉原で500円ほどで売っているWiFiモジュールです。マイクロプロセッサとFlash ROMも付いており、これにMicroPython(Pythonのサブセット)を移植した方がいて、発表者の方も試しに載せてみたとのこと。発表後に、太っ腹にも、2個のESP8266(MicroPython付)を、勉強会参加者にプレゼントするとのこと。興味があったので、ジャンケン大会に参加・勝利し、1つ頂きました。

MicroPython on ESP8266:起動

家に帰って、ESP8266の結線方法やMicroPythonの使い方などを調べたり、起動に必要な電源や線など用意するために数時間掛ってしまいましたが、無事、起動することができました。下記は、起動後に、テスト用のプログラムを入れて試したものです(素数生成プログラムを書いてみました)。
f:id:Itsukara:20160614231013j:plain

なお、上記に記載しましたように、ESP8266は、マイクロプロセッサ、Flash ROMにWiFiが付いたもので、GPIOなどの入出力端子や、AD変換端子もついてます。これに、色々なセンサーを付けてIoTに使うことを想定しているらしいです。通常の消費電流は80mAですが、DeepSleepモードでは1mA以下になります。また、内臓タイマーを使って、一定時間後にDeepSleepから復帰させることができます。

DeepSleepを使って、通常動作時間を1日に2分程度に留め、後はDeepSleepモードにしておけば、単3電池数本で、1年ぐらいは使えると思います。概算ですが、単三電池は約800mAh。80mAで10時間使える計算なので、1日2分しか使わなければ、300日使えということです。ちなみに、これとは別ですが、実は、ガラケーを目覚まし代わりに使っており、目覚ましで起きたら電源を切るようにしてます。動作時間は1日10分程度にしているおかげで、3カ月以上、バッテリーが持っています。

ESP8266周りの配線

勉強会でのプレゼンによると、通常、配線にはブリットボードというモノを使うらしいですが、手近になかったので、以前に買ったワニ口クリップにビニール線を半田付けして作った、急ごしらえの線で繋ぎました。(ちなみに、非常に扱い難いので、ブリットボードAmazonで別途注文済)
f:id:Itsukara:20160614231005j:plain

なお、結線方法を図にしてみましたので、トライする方は参考にしてください。ちなみに、I016端子とRST端子の結線により、タイマーでDeepSleepモードから復帰できます。また、MicroPythonから、DeepSleepモードを制御することができます。MicroPython on ESP8266は、Flash ROM上にmain.pyというファイルがあると、起動後に自動的に実行するようになってます。そこで、main.pyで定期的にセンサーからデータを集めて、直ぐにDeepSleepするようにすれば、IoT端末として長期間使えると思います。
f:id:Itsukara:20160614235738j:plain

ちなみに、電源および信号線となるUSB-UART変換器は、202HWを改造したときに買ったものがあり、流用できました。Web掲載の記事によっては、ESP8266が170mAも消費するので、別途、3.3V電源を繋げないと動作しないと書かれていたのですが、幸い、既に持っていたUSB-UART変換器(400円で購入)で、電源容量が間に合ったようです。

なお、色々と試したところでは、WiFiを使っても、USB側では0.08Aしか流れていなので、3.3V側も、170mAは行かないと思います。参考までに、USB側の電流を測定中の写真を載せます(赤で0.08Aと表示)。
f:id:Itsukara:20160614231008j:plain

なお、上記写真では、ESP8266にソケット的なものが刺さってますが、このソケットは、家にあった廃材(PCキーボード、マウス)から引っぺがしたものです。

MicroPython再インストール

上記で、「Flash ROMにmain.pyが載っていれば」と書いたのですが、実は、頂いたものでは、Flash ROM上にファイルシステムが構築されていませんでした。MicroPythonのサイトを見ると、Flash ROMの容量が1MB以下の場合は、ファイルシステムを作らないとのこと。

頂いたESP8266のFlash ROMの容量が中々わからなかったのですが、データシートをよく見たところ、4MB入っていることが分かりました。そこで、GitHubからソースコードを入手して、MicroPythonを再インストールすることしました。

まずは、VirtualBoxUbuntu環境を用意

MicroPythonを再インストールするには、Linux環境が必要で、Ubuntuが良さそうでした。これが無かったので、VirtualBoxUbuntu 14.04の環境を構築しました。

UbuntuにOpenSource ESP SDKをインストール

MicroPythonのGitHubでの説明に従って、まずは、UbuntuにOpenSource ESP SDKをインストールしました。ここで少しトラぶりました。下記だけでうまくいくはずなのですが、make中に「Python missing or unusable error」というエラーが出て止まってしまいます。

$ sudo apt-get install make unrar autoconf automake libtool gcc g++ gperf \
    flex bison texinfo gawk ncurses-dev libexpat-dev python python-serial sed \
    git unzip bash help2man
$ git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
$ cd esp-open-sdk
$ make

# install途中にエラー

これに関しての対応方法は、ここに書かれていました。書かれた内容に従って下記のようにしたところ、うまくいきました。

$ sudo apt-get install python2.7-dev
$ sudo apt-get install python3.4-dev
$ make

make完了後は、buildで作成されたbinディレクトリ(~/esp-open-sdk/xtensa-lx106-elf/bin)をPATHに追加する必要があります。

MicroPythonをmake

これは、GitHubの説明に従って下記のようにするだけでうまくいきました。

$ git clone https://github.com/micropython/micropython.git
$ cd micropython/
$ git submodule update --init
$ cd esp8266/
$ make axtls
$ make

UbuntuからCOM5が見えるようにする

MicroPythonをESP8266に書き込むためには、Ubuntuの載った仮想マシンから
USB-UART変換器経由でCOM5に接続する必要があります。これに関しては、この記事が参考になりました。つまり、USB-UART変換器でWindowsから見えるようになるCOM5をVirtualBoxに繋ぐのではなく、USB-UART変換器自体Ubuntuから見えるように、VirtualBoxでUSB機器としての接続設定をするということです。

一番迷ったのは、USB-UART変換器のドライバの入手方法です。いろいろ調べたのですが、実は、何もしなくても、Ubuntuが自動的に認識していることが分かりました。下記は、dmesgコマンドで確認した結果です。

$ dmesg
...
[  248.778569] usbserial: USB Serial support registered for cp210x
[  248.778638] cp210x 2-2:1.0: cp210x converter detected
[  249.174198] usb 2-2: reset full-speed USB device number 3 using ohci-pci
[  249.670905] usb 2-2: cp210x converter now attached to ttyUSB0

MicroPythonをESP8266に書き込み

最初に、flashをeraseした方が良いとのこと。これに必要なesptool.pyは、「~/esp-open-sdk/xtensa-lx106-elf/bin/esptool.py」にあります。そこで、「~/esp-open-sdk/xtensa-lx106-elf/bin/esptool.py」へのシンボリックリンクを「~/micropython/esp8266/」に置き、下記コマンドで、USB-UART変換器が繋がっていることを確認してから、fashをeraseしました。(/dev/ttyUSB0は、USB-UART変換器のデバイスファイルです)

$ sudo ./esptool.py --port /dev/ttyUSB0 chip_id
Connecting...
Chip ID: 0x00eexxxx

$ sudo ./esptool.py --port /dev/ttyUSB0 erase_flash
Connecting...
Erasing flash (this may take a while)...

ところが、一度eraseすると、UbuntuからUSB-UART変換器が見えなくなってしまいます。そこで、再度、VitualBoxの設定を変えて、UbuntuからUSB-UART変換器が見えるようにする必要がありました。

次に肝心の書き込みですが、Makefileでは、flash ROMのサイズが8Mbitと想定しているため、これを32Mbit(4MB)に変える必要があります。また、sudoでmake deployすると、esptool.pyのパスが認識されないため、これも修正が必要です。結局、次のように修正しました。

# Makefileのサイズ修正、パス修正
        $(Q)./esptool.py --port $(PORT) --baud $(BAUD) write_flash --flash_size=32m 0 $<
        #$(Q)esptool.py --port $(PORT) --baud $(BAUD) write_flash --flash_size=8m 0 $<

後は、次のようにデプロイして完了です。

$ sudo ./esptool.py --port /dev/ttyUSB0 chip_id
Connecting...
Chip ID: 0x00eexxxx

$ sudo make PORT=/dev/ttyUSB0 deploy
Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.
Writing build/firmware-combined.bin to the board
Connecting...
Erasing flash...
Took 3.22s to erase flash block
Wrote 505856 bytes at 0x00000000 in 59.6 seconds (67.9 kbit/s)...

Leaving...
$ 

その後

ファイル操作モジュール(pos.py)作成

上記までで、flash ROMに書き込めるようになったのですが、OSが無いため、「ls」「cd」「mkdir」「pwd」「cp」「mv」「rm」といったコマンドがなく、不便を感じました。また、Pythonのプログラムをflash ROMに書き込むには、プログラムを一度、文字列として変数に入れて、次のようなプログラムを打ち込んで書き込む必要があり、これも不便でした。

# 予め、変数sにプログラムの文字列を入れておく
>>>  with open("test.py", "w") as f:
...      f.write(s)

そこで、OSの非常に基本的な機能を、ある程度Linuxライクに使えるように、簡単なモジュールを作成しました。実は、MicroPythonには、「uos(Micro OS)」というモジュールが入っていて、importすると使えるのですが、関数の名称がLinuxと結構異なり、機能も不足しているので、これを補うためのモジュール「pos(Pico OS)」を作成しました。GitHubに置きましたので、中身を見たうえで、良かったら使ってください。

ちなみに、ESP8266は、RAMが50KBしかなく、MicroPythonが起動した状態では、22KBしか残っていません。そのため、色々なモジュールをインポートすると、RAMが結構減ってしまいます。これは気を付けてください。

起動後に定期的に出るメッセージの抑止方法

ESP8266を起動すると、定期的に下記のようなメッセージが表示されます。これは害がないとのことですが、邪魔になるので、下記により抑止できます。

# 表示されるメッセージ例
chg_A3:0

chg_A3:-180

chg_A3:0

chg_A3:-180

chg_A3:0

chg_A3:-180

# 抑止方法
>>> import esp
>>> esp.osdebug(None)  

WiFi接続方法

次のプログラムをflash ROMのファイルに入れておけば、これをimportして、do_connect()を呼び出すことで、WiFiに接続できます。

def do_connect():
    import network
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('connecting to network...')
        sta_if.active(True)
        sta_if.connect('無線LANルータのSIDを記載', '無線LANルータのパスワードを記載')
        while not sta_if.isconnected():
            pass
    print('network config:', sta_if.ifconfig())

WiFiの状態確認と停止の方法は下記です。

>>>  wlan.ifconfig()     # 状態確認
>>>  wlan.active(False)  # WANの停止

なお、一度WiFiで繋ぐと、次回起動時に、勝手にWiFiに繋がります。MicroPythonよりも下のレイヤーで、WiFiの設定を覚えており、自動的に繋ぐようです。

MicroPython on ESP8266の性能

性能測定のために下記の素数生成プログラムで試しました。1000までの素数の生成「gen_primes(10000)」で7秒ぐらい掛っています。ちなみに、2年前に買ったWindows PC上で測定したところ6.6ミリ秒だったので、1000倍ぐらい遅いですね。ただ、センサーのデータを取得するぐらいならば問題ないでしょう。

# MicroPythonで走らせたプログラム
import machine
rtc = machine.RTC()
rtc.datetime((2016, 6, 8, 0, 15, 35, 0, 0))

from math import sqrt
def gen_primes(n):
  primes = set([2, 3, 5, 7, 11, 13, 17, 19])
  for i in range(23, n+1, 2):
    for j in range(3, int(sqrt(n))+1, 2):
      if i % j == 0:
        break
    else:
      primes.add(i)
  return primes

import gc

def sec():
  dt = rtc.datetime()
  return (dt[4]*60 + dt[5])*60 + dt[6]+ dt[7]/1000.0
  
  
def time_gen_primes(n):
  a1 = gc.mem_alloc()
  s1 = sec()
  f1 = gc.mem_free()
  g = gen_primes(n)
  a2 = gc.mem_alloc()
  s2 = sec()
  f2 = gc.mem_free()
  print("gen_primes({}):{:.3f} sec, len:{}, alloc:{}, free:{}".format(n, s2 - s1, len(g), a2, f2))
  g = None
  gc.collect()

for n in range(1000, 30000, 1000):
  time_gen_primes(n)

PCでは以下のように測定。

# ファイル:gen_primes.py
from math import sqrt
def gen_primes(n):
  primes = set([2, 3, 5, 7, 11, 13, 17, 19])
  for i in range(23, n+1, 2):
    for j in range(3, int(sqrt(n))+1, 2):
      if i % j == 0:
        break
    else:
      primes.add(i)
  return primes

# 下記コマンドで測定。表示されるのは1000回の秒数なので、1回あたり6.6ミリ秒程度。
>>> import timeit
>>> timeit.timeit("gen_primes(10000)", setup='from gen_primes import gen_primes', number=1000)
6.574487369428709

今後

MicroPython on ESP8266を、IoTデバイスとして色々試したいと思っています。しかし、現状では配線がごちゃごちゃして扱いにくいので、USBで直結できるコンパクトなものをAmazonで買うことにしました。既に発注済みですが、中国から国際便で送られてくるために、届くまでに1~2週間くらい掛り、届くのは6月26日の予定です。

これに繋げるセンサーや、電源となるDC-DCコンバーター(電池から3.3Vに直接変換するため)、色々な配線など、ハード系の部品をAmazonで色々注文し、届くのを待っているところです。

Amazonで中国系の会社から部品を購入すると、届くまで時間が掛りますが、非常に安くて気軽に試せるので、これからも色々購入して試したいと思っています。

MicroPython on ESP8266を契機に、IoTのハードとソフトに興味を持ち始めた、今日この頃です。