はじめに
RaspberryPi Pico W で MicroPythonを使って、簡易Webサーバーを作る方法を解説します。
ソースコードは公式サイトのチュートリアル(Getting Started) と同じですが、日本語のコメントを追加して、少し細かい内容まで解説しています。
チュートリアルのコードを見て「これどういう意味?」と思った方はぜひご覧ください。
~ この記事の内容 / Contents ~
環境
この記事で使用する環境は以下の通りです。
環境 | バージョンなど | 備考 |
開発用PCのOS | Windows11 | Windows10でもOKです |
言語 | MicroPython | |
開発環境 Thonny | 3.3.13 | |
ボード | RaspberryPi Pico W |
RaspberryPi Pico Wとの接続
配線は必要ありません。Pico W 本体のWi-Fiモジュール・LED・温度センサを使います。
使用する部品
Pico W 本体
Raspberry Pi Pico W本体です。技適対応品かどうか心配…という方は、以下のページで出品者を「共立エレショップAセレクト」(国内の有名ショップ)にして購入してください。
USBケーブル Micro-B
本体にUSBケーブルが付属していないので、別途購入が必要です。PicoW側の形状は「Micro-B」、ひと昔前のスマホと同じタイプを使います。現在主流のTypeCではないので注意が必要です。
プログラム概要
今回のプログラムの概要は以下の通りです。
- Pico W をWi-Fiに接続する
- ソケットを作成する
- ブラウザからの接続待ちをする
- Webサイトを生成してブラウザに送信する
- (リクエストに応じて、LED操作と・Webサイトの生成をする)
実行結果
後述するプログラムを実行すると、PicoWが簡易のWebサーバーになります。
表示されるWebページでは、本体のLEDの操作や、温度センサーの温度を取得することができます。Webページなのでスマホからの操作も可能です。
利用環境によって、Pico W のIPアドレスは変わります。
IPアドレスは、プログラムの実行時の出力されるので、Thonnyなどの開発環境の出力を確認してください。
全体コード
全体コードは以下の通りです。詳細な内容は後述する「コードのポイント」で解説します。
import network
import socket
from time import sleep
from picozero import pico_temp_sensor, pico_led
import machine
#
# Wi-Fi ルーターのSSIDとパスワードです。
# お使いの設定に書き換えてください。
#
ssid = 'NAME OF YOUR WIFI NETWORK'
password = 'YOUR SECRET PASSWORD'
#
# Wi-Fiに接続する関数です
#
def connect():
#Connect to WLAN
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
while wlan.isconnected() == False:
print('Waiting for connection...')
sleep(1)
ip = wlan.ifconfig()[0]
print(f'Connected on {ip}')
return ip
#
# WEBページを生成する関数です
#
def webpage(temperature, state):
#Template HTML
html = f"""
<!DOCTYPE html>
<html>
<form action="./lighton">
<input type="submit" value="Light on" />
</form>
<form action="./lightoff">
<input type="submit" value="Light off" />
</form>
<p>LED is {state}</p>
<p>Temperature is {temperature}</p>
</body>
</html>
"""
return str(html)
#
# クライアント(ブラウザ)からの接続に対応する関数です
#
def serve(connection):
#Start a web server
state = 'OFF'
pico_led.off()
temperature = 0
while True:
client = connection.accept()[0]
request = client.recv(1024)
request = str(request)
try:
request = request.split()[ 1]
except IndexError:
pass
if request == '/lighton?':
pico_led.on()
state = 'ON'
elif request =='/lightoff?':
pico_led.off()
state = 'OFF'
temperature = pico_temp_sensor.temp
html = webpage(temperature, state)
client.send(html)
client.close()
#
# データをやり取りする口(ソケット)を
# 作成する関数です
#
def open_socket(ip):
# Open a socket
address = (ip, 80)
connection = socket.socket()
connection.bind(address)
connection.listen(1)
return connection
#
# メインの処理部分です
#
try:
# Wi-Fiに接続し、IPアドレスを取得します
ip = connect()
# IPアドレスを使って、データをやり取りするソケットを作ります
connection = open_socket(ip)
# ソケットを使って、クライアント(ブラウザ)からの接続を待ちます
# (内部で無限ループ)
serve(connection)
#
# プログラムが中断された場合は、この処理に飛び、
# デバイスをリセットします
#
except KeyboardInterrupt:
machine.reset()
Pico/Pico Wだけ(PCなし)でプログラムを実行する場合は、ファイル名を「main.py」にして、Pico/Pico W本体に保存してください。
コードのポイント
【メイン処理】Wi-Fiへの接続
メインの処理部分から、connect関数を呼び出します。
#
# メインの処理部分です
#
try:
# Wi-Fiに接続し、IPアドレスを取得します
ip = connect()
connect関数
端末モードで作成
connect関数ではまず、Wi-Fiを使うための「wlan」オブジェクト(変数)を作成します。引数「STA_IF」は、Pico Wが「端末」としてルーターに接続するための指定です。
他にも「network.AP_IF」が指定できますが、こちらはPico Wを「アクセスポイント」として指定するためのもので、ルーターの代わりに、Pico WにPCやスマホなどを接続させる場合に使用します。
active関数はネットワークインターフェイスを有効化するためのもので、接続や通信を行う前に「True」に指定します。
#
# Wi-Fiに接続する関数です
#
def connect():
#Connect to WLAN
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
Wi-Fiへの接続
wlan.connect関数を実行するとWi-Fiへの接続処理が始まります。
この関数は、接続が完了する前に次の行に進むため、isconnect関数を使って、接続が完了するまで(戻り値がFalseの間)、ループで待機するようにしています。
接続が完了すると、ifconfig関数のゼロ個目の要素にPico WのIPアドレスが返されるので、そのIPアドレスをメインの処理に返します。
wlan.active(True)
wlan.connect(ssid, password)
while wlan.isconnected() == False:
print('Waiting for connection...')
sleep(1)
ip = wlan.ifconfig()[0]
print(f'Connected on {ip}')
return ip
【メイン処理】ソケットの作成
Wi-Fiへの接続処理が完了したら、open_soket関数で、ネットワークを介してクライアント(ブラウザ)とデータをやり取りするための「ソケット」を作成します。
#
# メインの処理部分です
#
try:
# Wi-Fiに接続し、IPアドレスを取得します
ip = connect()
# IPアドレスを使って、データをやり取りするソケットを作ります
connection = open_socket(ip)
open_socket関数
ソケットにはconnect関数で取得したIPアドレスと、ポート番号を指定します。ポートはWebやメールなど、用途ごとに番号が決まっており、今回はWeb用の「80」を指定します。
bind関数はIPとポートをソケットに設定、listen関数はデータのやり取りを開始するために呼び出します。
#
# データをやり取りする口(ソケット)を
# 作成する関数です
#
def open_socket(ip):
# Open a socket
address = (ip, 80)
connection = socket.socket()
connection.bind(address)
connection.listen(1)
return connection
【メイン処理】ブラウザからの接続待ち
ソケットを作成してデータをやり取りする準備が完了したら、serve関数を使ってブラウザからの接続待ちと、応答処理を行います。
#
# メインの処理部分です
#
try:
# Wi-Fiに接続し、IPアドレスを取得します
ip = connect()
# IPアドレスを使って、データをやり取りするソケットを作ります
connection = open_socket(ip)
# ソケットを使って、クライアント(ブラウザ)からの接続を待ちます
# (内部で無限ループ)
serve(connection)
serve関数
初期化
接続待ちを行う前に、Pico W本体のLEDと状態を「消灯」で初期化し、温度用の変数の値もゼロで初期化します。
#
# クライアント(ブラウザ)からの接続に対応する関数です
#
def serve(connection):
#Start a web server
state = 'OFF'
pico_led.off()
temperature = 0
※ 本体LEDの操作については、こちらのpico_ledについての記事もご覧ください。
接続待ち
accept関数を使ってブラウザからの接続待ちを行います。この関数は接続があるまで次の処理に進まないため、ループで待つ必要はありません。
接続があったら(処理が次の行に進んだら)、ブラウザからのデータを受信して、文字列に変換します。
while True:
client = connection.accept()[0]
request = client.recv(1024)
request = str(request)
受信データの解析(初回表示)
split関数は、文字列をスペース区切りで分割する関数です。また、関数の末尾に[ 1]がついているため、分割した1番目の文字列のみが「request」に代入されます。
try:
request = request.split()[ 1]
except IndexError:
pass
実際に、ブラウザから初めてアクセスした場合、以下の受信データが届きます。スペースで区切られて「GET」「/」の順番なので、「request」には「/」が代入されます。
b'GET␣/␣ HTTP/1.1 … ※ 以降省略
※ []の数字は0番目から始まるため、1番目は「/」になります。
LED操作とWebページの作成・送信
以降の処理では「request」の内容に応じて、LEDの操作を行います。
前述した初回表示の受信データでは「request」の内容は「/」であり、条件に合致しないので操作は行われません。
temp関数では、PicoW本体の温度センサの温度を取得できます。
取得した温度とLEDの状態は、webpage関数に渡しブラウザに表示するWebページの作成に使用します(詳細は後述)。作成したWebページ(html)はclient.send関数でブラウザに送信します。
送信後は、ブラウザとの接続を切断し、無限ループによって冒頭のaccept関数に戻ります。
if request == '/lighton?':
pico_led.on()
state = 'ON'
elif request =='/lightoff?':
pico_led.off()
state = 'OFF'
temperature = pico_temp_sensor.temp
html = webpage(temperature, state)
client.send(html)
client.close()
webpage関数
ブラウザに表示するWebページを生成する関数です。HTML言語でWebページを作成しており、ブラウザで見ると以下のような操作ボタン(Submit)付のページが表示されます。
{tempreture}などの{}で囲まれた部分は「プレースホルダ」とよばれ、引数で与えた温度・LEDの状態が自動的に代入されます。
#
# WEBページを生成する関数です
#
def webpage(temperature, state):
#Template HTML
html = f"""
<!DOCTYPE html>
<html>
<form action="./lighton">
<input type="submit" value="Light on" />
</form>
<form action="./lightoff">
<input type="submit" value="Light off" />
</form>
<p>LED is {state}</p>
<p>Temperature is {temperature}</p>
</body>
</html>
"""
return str(html)
Webページの操作ボタンが押された時の流れ
前述の内容では、Webページの初回表示の流れを解説しましたが、ここではWebページの「LightOn」ボタンが押された時の流れを解説します。
Webページからのリクエスト送信
Webページ上のボタンは、以下のHTML対応しており、ユーザーがボタンを押すと、以下の「./lighton」の内容が「/lighton?」という文字列に自動変換されてPicoに送られます。
<form action="./lighton">
<input type="submit" value="Light on" />
</form>
b'GET␣/lighton?␣ HTTP/1.1 … ※ 移行省略
Pico W での受信処理
リクエストが送信されると、Pico Wではaccept関数での接続待ちが解除され、クライアントからのデータがsplit関数で処理されます。
今回の受信データは、「GET」「/lighton?」の順になるため、requestには「/lighton?」が代入されます。
これにより、if文に合致し、led_off関数でPico W 本体のLEDが点灯、stateの状態が”ON”になったWebページが生成されて、ブラウザに送られます。
request = request.split()
~~~ 省略 ~~~
if request == '/lighton?':
pico_led.on()
state = 'ON'
elif request =='/lightoff?':
pico_led.off()
state = 'OFF'
temperature = pico_temp_sensor.temp
html = webpage(temperature, state)
client.send(html)
client.close()
まとめ
RaspberryPi Pico W で MicroPythonを使って、簡易Webサーバーを作る方法を解説しました。
コード・解説ともに長くなってしまいましたが、チュートリアルのコードを見て「なぜ?」と思った方の参考になればうれしいです。
お知らせ
MicroPythonのプログラミングガイドブックが遂に発売!
「MicroPython」の本が遂にでました。
この一冊で、MicroPythonの言語仕様から、プログラミングの仕方まで”ガッツリ”学べます!。
内容は、普段別言語で開発している人や、これからマイコンを始める(工学系の)学生を対象としているので「初心者向け」ではありません。しかし、「自前のライブラリの作成」が目標なので、これ一冊で「ガッツリ」とMicroPythonを学ぶことができます。
全ての内容はここでは紹介しきれないので、詳細は以下のAmazonページをご覧ください。目次だけでも”ガッツリ”なのが確認できると思います。
Pico/Pico W関連のおすすめ本
RaspberryPi Pico / Pico W関連のおすすめ本を独断と偏見で3つ選んでみました。Picoやるならとりあえずこれ買っとけ的な本や、電子工作全般で使える本などを厳選しています。
質問・要望 大歓迎です
「こんな解説記事作って」「こんなことがしたいけど、〇〇で困ってる」など、コメント欄で教えてください。 質問・要望に、中の人ができる限り対応します。
使えたよ・設定できたよの一言コメントも大歓迎。気軽に足跡を残してみてください。記事を紹介したい方はブログ、SNSにバシバシ貼ってもらってOKです。