スキップしてメイン コンテンツに移動

ESP32 MicroPython webサーバ~Lチカ

 

今回はESP32でMicroPythonからWi-Fiを使ってwebサーバを立てたり、その応用をやってみようと思います。

TODO:前の記事の作成とURL張り ESP32でMicroPythonを使えるようにするまでの記事があります。すでに書き込みと実行が出来る前提で話を進めていくので、そちらの記事も合わせて読んで頂けると嬉しいです。

準備したもの

  1. ESP32
  2. LED
  3. ブレッドボード
  4. ジャンパワイヤ
  5. 抵抗(10kΩ,22kΩ)



webサーバを立てる

まずはwebサーバを立ててみましょう。

公式に情報が載っているのでこちらを参考に行っていきます。

micropython-docs-ja.readthedocs.io

↑のリンクにwebサーバを立てるシンプルなプログラムが載っているのでこれを参考にWiFiに接続して、ステーションモード(子機)として動くプログラムを作ります。

最初はとりあえず短く、最小限でhello worldをやってみました。

import network
import socket

#///WiFiに接続
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
print('connecting to network...')
sta_if.connect('SSID', 'PASS')#SSIDとPASSは書き換えが必要
while not sta_if.isconnected():
    pass
#///

#///ソケットの作成・登録・接続準備
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('listening on', sta_if.ifconfig()[0])
#///

#///ソケットの接続待機・送信・切断
while True:
    cl, addr = s.accept()
    print('client connected from', addr)
    response = '<h1>HELLO WORLD</h1>'
    cl.send(response)
    cl.close()
#///

このプログラムを使うときは以下の行を説明に従って書き換えて使用してください。

sta_if.connect('SSID', 'PASS')#SSIDとPASSは書き換えが必要

次の箇所を書き換えてください 。 以降のプログラムでも同じように書き換える必要があります。

  • SSIDを読者が使っているWiFIの名前(SSID)
  • PASSを読者が使っているWiFIのパスワード

  • 注意:この時点で先ほどのプログラムを実行してconnecting to network...と表示されてから動かない場合は書き換えたSSIDとPASSのスペルミスなどを確認してください。

プログラムの書き込み方はREPLに直接コピペする方法かファイルとしてアップロードして頂く方法どちらでも結構ですが、ファイルとしてアップロードした場合は各自で付けてもらったファイル名.pyとなっていると思うので、アップロード後REPLに

import ファイル名

とするだけで実行してくれます。いちいちインポートして関数を呼び出すのが(コマンドの履歴が残らないのもあって)面倒だったので以降でもこの方式をとらせていただきます。

WiFIに接続するためのプログラムは下のリンクを参考にしました。

micropython-docs-ja.readthedocs.io

ローカルの WiFi ネットワークに接続するには、次の関数を流用してください

と書かれているのでこちらをシンプルに書き換えたのが#///WiFiに接続とコメントに書かれている箇所にあたります。

参考にしたシンプルなプログラムでは

addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
省略
print('listening on', addr)

となっていますが、これだとaddrsocket.getaddrinfoで設定したIPアドレス0.0.0.0(ローカルマシン上のすべてのIPアドレス)が代入されてしまい、listening on 0.0.0.0と表示されてしまうので、割り当てられたIPアドレスがわかりやすく表示されるように

print('listening on', sta_if.ifconfig()[0])

としてあります。

筆者は勉強不足で知らなかったのですが、プログラム中で出てくるsocketbindなどの言葉はサーバサイドのプログラムでは当たり前の用語だそうです。

プログラムの流れや用語について勉強になったので参考にさせて頂いたサイトのリンクを貼っておきます。プログラムの書式は今回の内容に関係ないのでそこだけは注意してください。

qiita.com

プログラム実行後はWiFi接続に成功すると必ず、

listening on 192.168.X.X

と出力されるので、ブラウザを開きxの部分は各自の環境に合わせて出力される内容と同じく書き換えてください。(以降は同じような説明を省きます。)

http://192.168.x.x

にアクセスするとHELLO WORLDと表示されると思います。



webでピンの情報を見よう

実は前項の参考にしたシンプルなプログラムはピンの状態(HIGH/LOW)をwebに表示してくれるプログラムでした。

なので、一部変更しつつ最低限動くようなプログラムを以下に示します。

import network
import socket
from machine import Pin

available_pins = [4, 5, 12, 13, 14, 15, 18, 19, 21, 22, 23, 25, 26, 27]
pins = [Pin(i, Pin.IN, Pin.PULL_UP) for i in available_pins]

html = """<!DOCTYPE html>
<html>
    <head> <title>ESP32 Pins</title> </head>
    <body> <h1>ESP32 Pins</h1>
        <table border="1"> <tr><th>Pin</th><th>Value</th></tr> %s </table>
    </body>
</html>
"""

#///WiFiに接続
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
print('connecting to network...')
sta_if.connect('SSID', 'PASS')#SSIDとPASSは書き換えが必要
while not sta_if.isconnected():
    pass
#///

#///ソケットの作成・登録・接続準備
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('listening on', sta_if.ifconfig()[0])
#///

#///ソケットの接続待機・送信・切断
while True:
    cl, addr = s.accept()
    print('client connected from', addr)
    rows = ['<tr><td>%s</td><td>%d</td></tr>' % (str(p), p.value()) for p in pins]
    response = html % '\n'.join(rows)
    cl.send(response)
    cl.close()
#///

私も動かして初めてわかったのですが、初期状態でピンのプルアップが有効になっていました。

Pin(i, Pin.IN, Pin.PULL_DOWN)

とするとプルダウンが有効になって値が反転します。

リファレンスには手動で設定していたので意外でしたが、経験不足なのでこれが普通なのかはわかりません。

micropython-docs-ja.readthedocs.io

↑のリンクのリファレンスに載っているようにHIGH/LOWの入出力は以下のピンが使用可のです。

0-19, 21-23, 25-27

このピンはプルアップ、プルダウン両方とも問題なく使えました。

  • 注意:ピン0, 2番は起動時のモード設定に関わっているため、使用する際は注意してください。下に参考サイトを載せるのでESP32-WROOM-32 の概要→起動モードを参照してください。

ht-deko.com

プログラムで設定しているピンは上記の注意で示したピン以外を指定しています。

  • 番号が離散的なため、配列に一旦格納して使っているので、ピンの番号と配列の添え字の対応に注意してください。(以降説明を省く)

プログラム実行後、以下のようににアクセスして実際に動かしてみると、プログラム通りであればプルアップされているので何も配線していなけれなすべて1で表が作成されます。

http://192.168.x.x

使えるピンから適当に選んだ4番ピンとジャンパワイヤでGNDにつなぐいでリロードすると

Pin(4)が0になっていれば成功です。



URLでピンの制御をしてみよう

以下の項を書くのにあたって参考にさせて頂いたサイトのリンクを下に貼っておきます。

qiita.com

URLでピン制御をしようと思ったとき中途半端に知識がついてしまうと、過信して「どんなクエリが来たか返してくれる関数があるだろう」と思ってしまい、無駄に時間をかけて調べてしまいました。(Arduino core for the ESP32のライブラリにはあるので)

リファレンスと上記のサイトを見るとsocket.recv(bufsize)という関数があり、これでクライアントからデータの塊を受信して、その塊の中からクエリの部分だけを手動トリミングする方法をとっていました。他に方法が見つからないためこちらを採用させていただきました。

あくまで通信をするためのモジュールだったことに気付けませんでした。

また、

re.search(r'on=([+-]?\d+)', request)

などの[+-]?\d+は以下を参考にさせて頂き、整数の正規表現を意味しています。

qiita.com

LEDを制御しよう

まずは回路で、同じように組んでみてください。

使用した抵抗は10kΩです。

今回はHIGH/LOWの出力なので前回と同じピン番号の集合が指定されています。

import network
import socket
import time
from machine import Pin 
import re

available_pins = [4, 5, 12, 13, 14, 15, 18, 19, 21, 22, 23, 25, 26, 27]
pins = [Pin(i, Pin.OUT) for i in available_pins]#使えるピン番号が離散的なので配列の添え字とピンの番号の対応に注意

#///WiFiに接続
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
print('connecting to network...')
sta_if.connect('SSID', 'PASS')#SSIDとPASSは書き換えが必要
while not sta_if.isconnected():
    pass
#///

#///ソケットの作成・登録・接続準備
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('listening on', sta_if.ifconfig()[0])
#///

def change_led_st(qs, st:bool):
    qs_num = int(qs.group(1))
    pins[qs_num].value(st)
    status = 'ON' if st == True else 'OFF'
    response = '<h1>ESP32 LED {} : {} Pin</h1>'.format(status, available_pins[qs_num])
    cl.send(response)

while True:
    cl, addr = s.accept()
    print('client from', addr)
    request = str(cl.recv(1024)).lower()
    qs_on = re.search(r'on=([+-]?\d+)', request)
    qs_off = re.search(r'off=([+-]?\d+)', request)

    if qs_on != None:
        change_led_st(qs_on, True)
    elif qs_off != None:
        change_led_st(qs_off, False)
    else:
        response = '<h1>ESP32 LED ERROR</h1>'
        cl.send(response)

    cl.close()

上記のプログラムを書き込んだ後、実行して正常に動作するとlistening on 192.168.x.xと出力されるので、ブラウザを開きxの部分は各自の環境に合わせて出力される内容と同じく書き換えて

http://192.168.x.x/on=5

にアクセスすると

このように表示されるのでLEDを確認すると光っていると思います。

また、

http://192.168.x.x/off=5

にアクセスするとLEDが消えると思います。

URLの最後の数字を0~15(配列の添え字番号)に変えることによって対応するピンのHIGH/LOWを切り替えられるようになっています。

上の例ではURLで5を指定しているので入れるの6(5+1)番目に格納されている15番ピンの制御をしてみました。

指定するピンを変える場合は抵抗につながっているジャンパワイヤを対応するピン番号に差し替えるのを忘れないで下さい。



webページでアナログ値を見よう

まずは回路図と同じように組んでみてください。

しっかりと測れているのか検証するために分圧した電圧を計測します。

抵抗の場所を入れ替えて2通りの測定をしてみます。

読者の方は両方か一方どちらでも構いません。

測定法:A(10kΩの電圧測定) 

測定法:B(22kΩの電圧測定) 

使っている抵抗は10kと22kΩなので違う抵抗値を使う場合は値が変わるので注意してください。

アナログ値を読み取る際は注意が必要で前項までとは違うピンを使用します。

micropython-docs-ja.readthedocs.io

上記のリファレンスにもあるように32~39番でしかアナログ値を読むADCが使えないのに加え、以下に注意してください。

  • esp32には内部にホールセンサを搭載しているので、これと併用する場合にはADCの36、37番ピンは同時に使えないので注意が必要です。参考リンクを下に貼っておきます。
  • 37、38番ピンは組み込まれている回路で既に使用されているのでピンとして引き出されていない。
  • 36、39番ピンはそれぞれVP、VNピンに割り振られている。

trac.switch-science.com

esp32.com

import network
import socket
import time
from machine import Pin, ADC
import re

adc_pins = [ADC(Pin(i)) for i in (32, 33, 34, 35,  36, 39)]#ADCに使えるピンが決まっているので、配列の添え字(0~7)とピン(32~39)の番号の対応に注意
for i in adc_pins:
    i.atten(ADC.ATTN_11DB)

#///WiFiに接続
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
print('connecting to network...')
sta_if.connect('SSID', 'PASS')#SSIDとPASSは書き換えが必要
while not sta_if.isconnected():
    pass
#///

#///ソケットの作成・登録・接続準備
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('listening on', sta_if.ifconfig()[0])
#///

def pin_read(qs):
    qs_num = int(qs.group(1))
    val = adc_pins[qs_num].read()
    response = '<h1>ESP32 Read {} Pin : {}</h1>'.format(qs_num+32, val)
    cl.send(response)

#///ソケットの接続待機・送受信・切断
while True:
    cl, addr = s.accept()
    print('client connected from', addr)
    request = str(cl.recv(1024)).lower()
    qs = re.search(r'read=([+-]?\d+)', request)

    if qs != None:
        pin_read(qs)
    else:
        response = '<h1>ESP32 READ ERROR</h1>'
        cl.send(response)

    cl.close()
#///

上記のプログラムでは

adc_pins = [ADC(Pin(i)) for i in range(32, 40)]#ADCに使えるピンが決まっているので、配列の添え字(0~7)とピン(32~39)の番号の対応に注意
i.atten(ADC.ATTN_11DB) for i in adc_pins:

として、11dBの減衰率を設定することで、約3.6Vまでの12bitアナログ値を取得できるようにしています。

本来は1V12bitがデフォルトで入力電圧と戻り値のbit数も変更できるようなのでADCピンの説明で載せたリンクのリファレンスを参考にしてください。

esp32のADCは公式がある程度の誤差があると言っているのでそこも注意する必要があります。

esp32.com

実際にアナログ値が取れているか確認してみましょう。

使う抵抗は22kΩと10kΩの二種類で3.3Vを分圧して測ってみます。

プログラム実行後、

http://192.168.x.x/read=1

とすると、URL最後の数字がadc_pins配列の添え字番号にあたるので、今回は1を指定しているので配列二番目の33番ピンを読み取ることを意味しています。

測定法:A 

測定法:B 

左側が理想の値とマルチメータを使った計測で右側がwebページに表示されたアナログ値を電圧に変換したものになります。

結果はこんな感じで、計算は以下サイトのように計算しました。

分解能は12bit計算です。

hegtel.com

miraiworks.org

表の同じ色がついている箇所が重要なので注目してみると少しのずれはありますが、概ね良いアナログ値がとれているので問題なく動作していると思います。

esp32の最大測定電圧が約3.6Vとリファレンスに書かれていたのでアナログ値から変換する計算で3.6Vを使用したことによる誤差(それぞれ3~4%程度)が大きくなっているんだと思います。



まとめ

  • クエリストリングで操作できるようにすることで応用しやすくなった。
  • サーバ側の基本的な処理の流れを学ぶことが出来た。

    コメント

    このブログの人気の投稿

    Phaser3 + Typescriptを使ってRPGゲームの基礎を作ろう!その2

    前回の記事 に引き続きPhaser3+Typescriptを使って RPG の基礎を作っていきます。 この記事は前回の記事を呼んだ前提で説明していきますので、ぜひそちらを先に読むことをお勧めします。 また、今回の記事は前回よりも難易度と内容量が上がっていますが、記事の最後に作業後の リポジトリ のリンクがありますので、そちらを先にダウンロードしてそちらと比較しながら学習することができます。 前回まで作成した状態のプログラムが以下の リポジトリ からダウンロードできますので前回まででうまくいっていない方は参考にしてみてください。 github.com 最終目標(再掲) ・Phaser3とTypescriptで RPG ゲームの基礎を作る。 ・Phaser3をTypescriptで使う方法を学ぶ ・Phaser3の使い方を学ぶ 今回の目標 ・ゲームの作り方の断片を知る ・キャラの表示できるようにする ・キャラを移動できるようにする ・当たり判定をつける ・ NPC の追加してみる ・話しかけられるようにする 開発前提(再掲) ・Nodejsの環境・知識がある ・ Javascript ・Typescriptがある程度かける ・当ページ紹介の環境を試す場合はgit・ github の知識がある 使用した主要Nodeモジュール(再掲) ・typescript(Typescriptの コンパイル 用) ・phaser(フロントの Javascript 用ゲームライブラリ) ・live-server(ソースを監視してブラウザのページをリロード) ・ts-loader(webpackがTypescriptをバンドルする用)webpack(言わずと知れたモジュール依存をいい感じに解決しバンドルする) ・webpack- cli (webpackを コマンドライン で使用するのに必要) 注:各Nodeモジュールバージョンは後述 当ブログ仕様の画像素材の注意点 当ブログで使用する画像素材は『 ピポヤ倉庫 』より許可なしで無償再配布・改変が認められたものを改変して作成されたものです。 中には許可なく再配布・改変してはいけない素材もインターネット上には多く存在するのでそれらを使用するときは十分に規約を呼んでから使用しましょう。 1. キャラの表示 想定画面 今回は、はじめにキャラつまり操作

    M5Stackで、においセンサー(TGS2450)を使ってみる。(LCDに表示編)

    今回は、においセンサー(TGS2450)から取得したデータをM5Stackの LCD にグラフ表示をしていきます。 今回主に使用したもの M5Stack 10Ω抵抗 5本 Pch  MOSFET  2SJ334(スイッチとして利用) M5StackSideBB(ブレッドボード) M5Stackのピンはそのままだと配線するとき手間がかかるかと思いますので、今回はM5StackSideBBを利用しました。 ブレッドボードも付いているので、手軽に電子工作できるので個人的にオススメです。 SideBB for M5Stack www.switch-science.com 諸注意 本ブログのプログラムはArduinoIDEまたはPlatformIOでM5Stackの開発ができる状態であること前提のものですので、各自導入をお願いします。 過去に Windows のArduinoIDEで M5Stackの開発環境を構築する記事 があるので、参考にしてください。 においセンサー(TGS2450)について 今回使用したセンサーは 秋月電子通商 で購入できます。 http://akizukidenshi.com/catalog/g/gP-00989/ akizukidenshi.com TGS2450には4つのピンがあり、使用するのはそのうちの3本でヒーターを温めるピン、センサー情報を得るピン、GNDがあります。 ここで注意したいのは、ヒーター電圧は1.6Vと記述されているのでM5Stackで利用するときは分圧して3.3Vを1.6V近くまでに降圧することをお勧めします。 TGS2450のセンサーは可変抵抗になっており、データシートには5.62kΩ〜56.2kΩの間を抵抗が変化し、においが強いとより低くなると記述されていました。 よって、センサーの値をM5Stackで取得するにはセンサー抵抗と外部に接続する任意の値の抵抗による分圧回路によって求めることができます。 TGS2450の動作方法は、250ms周期でセンサーに電圧を5ミリ秒on、245ミリ秒offの状態にしてヒーターは8ミリ秒on、242ミリ秒offにすることで値を取得します。 配線 においセンサーとM5Stackの配線 上の図が配線図です。 30Ωの抵抗は10Ωの抵抗を三つ直列に接続して作成しました。 また、上部の抵抗10Ω二

    Phaser3 + Typescriptを使ってRPGゲームの基礎を作ろう!その1

    今回はPhaser3とTypescriptを使って簡単な RPG ゲームを作る方法を紹介していきます。 内容はPhaser3およびゲーム作りについての記事なので、Nodejsの周辺モジュールなどの説明は一部省いての説明になりますのでご了承ください。 またこの記事では Phaser2 ではなく Phaser3 を使用するので注意してください。 この記事は二部構成になりますので、この記事を読んだ際はぜひ次の記事も読むことをお勧めします。 最終目標 ・Phaser3とTypescriptで RPG ゲームの基礎を作る。 ・Phaser3をTypescriptで使う方法を学ぶ ・Phaser3の使い方を学ぶ 今回の目標 ・開発環境を整える ・Phaser3の開発構成を知る ・スタート画面を作る ・マップ表示をさせる 開発前提 ・Nodejsの環境・知識がある ・ Javascript ・Typescriptがある程度かける ・当ページ紹介の環境を試す場合はgit・ github の知識がある 使用した主要Nodeモジュール ・typescript(Typescriptの コンパイル 用) ・phaser(フロントの Javascript 用ゲームライブラリ) ・live-server(ソースを監視してブラウザのページをリロード) ・ts-loader(webpackがTypescriptをバンドルする用)webpack(言わずと知れたモジュール依存をいい感じに解決しバンドルする) ・webpack- cli (webpackを コマンドライン で使用するのに必要) 注:各Nodeモジュールバージョンは後述 1. 最低限の開発環境の準備 今回最低限の環境を整えるために、『Typescript + Phaser3』の開発テンプレートを github リポジトリ で公開しました。 以下からZIPをダウンロードするか、 git clone コマンドで各自環境に展開してみてください。 ここから先は リポジトリ のプログラムを元に説明していきます。 github.com 展開するとファイル構造は以下のようになっているかと思われます。 注: 他にもファイルやフォルダがあるかと思われますが、表記されているのは今回使うものになっています。 - src/ (これから書くプログラムの保存領域)