第11章 Pico WのWebサーバー
🔒 研修参加者向け
この章は研修参加者向けです
この章では、Pico WをWebサーバーとして動かし、PCのブラウザーから外付けLEDを操作する仕組みを扱います。
研修参加者の方は、講師から案内されたパスワードを入力してください。
この教材を利用した研修については、お問い合わせください。
この章の目的
Section titled “この章の目的”第10章では、Pico WをWi-Fiへ接続し、 ネットワーク上の住所となるIPアドレスを確認しました。
この章では、Pico Wを簡易的なWebサーバーとして動かします。 PCのブラウザーへ操作画面を表示し、 画面上のボタンからGP15の外付けLEDを点灯・消灯します。
配線は第5章の外付けLED回路をそのまま使用します。 プログラムを入れ替えることで、 同じ出力部品をネットワーク経由で操作します。
この章でできるようになること
Section titled “この章でできるようになること”- WebブラウザーとWebサーバーの役割を説明できる
- HTTPのリクエストとレスポンスの流れを説明できる
- Pico Wで簡易Webサーバーを起動できる
- Shellへ表示された完全なURLをPCのブラウザーで開ける
- ブラウザーのボタンからGP15のLEDを点灯・消灯できる
- ローカルのWebサーバーとクラウドサービスの違いを説明できる
使用するもの
Section titled “使用するもの”| 分類 | 部品・環境 | 数量・条件 |
|---|---|---|
| マイコン | Raspberry Pi Pico WまたはPico WH | 1 |
| 出力回路 | 第5章で作成したGP15の外付けLED回路 | 1式 |
| PC接続 | データ通信対応Micro USBケーブル | 1 |
| ネットワーク | 事前確認済みの2.4GHz Wi-Fi | 1 |
| 操作端末 | Webブラウザーを使用できるPC | 1 |
開始時は、次の状態を確認します。
- 第5章 LEDで学ぶデジタル出力の GP15外付けLED回路が完成している
- PCとPico Wを同じ研修用Wi-Fiへ接続できる
- Wi-Fiが端末間通信を禁止する設定になっていない
- 第10章のプログラムでPico WのIPアドレスを取得できる
安全・セキュリティ上の注意
Section titled “安全・セキュリティ上の注意”- 配線を確認するときは、Pico WからUSBケーブルを外します
- GP15とLEDの間には、必ず330Ω抵抗を直列に接続します
- SSIDとパスワードを画面共有、提出物、写真へ残しません
- この章のWebサーバーには、利用者認証や通信の暗号化がありません
- 研修用の同一ネットワーク内だけで使用し、インターネットへ公開しません
- ルーターのポート転送や外部公開の設定は行いません
このプログラムは、HTTP通信の流れを理解するための研修用サンプルです。 業務システムとして公開するWebサーバーではありません。
11-1 WebブラウザーとWebサーバー
Section titled “11-1 WebブラウザーとWebサーバー”Webページを表示するときは、 情報を要求する側と、要求に応じる側に役割が分かれます。
| 役割 | この章の機器 | 主な動作 |
|---|---|---|
| クライアント | PCのWebブラウザー | ページやLED操作を要求する |
| Webサーバー | Pico W | 要求を受け取り、画面を返す、またはLEDを操作する |
ブラウザーから送られる要求をHTTPリクエスト、 Webサーバーから返される内容をHTTPレスポンスと呼びます。
この章では、次の順番で通信します。
- ブラウザーがPico WのIPアドレスへ接続します
- Pico Wが、LED操作用のHTML画面を返します
- ブラウザーが、点灯または消灯の要求を送ります
- Pico WがGP15の出力を切り替えます
- Pico Wが更新後の画面へブラウザーを戻します
図11-1 PCのブラウザーからPico WのLEDを操作する流れ
11-2 URLと処理を対応させる
Section titled “11-2 URLと処理を対応させる”Webサーバーは、リクエストに含まれる メソッドとパスを見て処理を分けます。
| メソッド | パス | この章での処理 |
|---|---|---|
GET | / | LED操作画面を表示する |
POST | /led/on | GP15をHIGHにしてLEDを点灯する |
POST | /led/off | GP15をLOWにしてLEDを消灯する |
GETは画面などの情報を取得する処理、
POSTは状態を変更する操作として使い分けます。
LEDを操作した後、Pico Wは303 See Otherという応答を返します。
この応答を受けたブラウザーは/を再取得するため、
操作後のLED状態が画面へ反映されます。
11-3 LED回路を確認する
Section titled “11-3 LED回路を確認する”新しい配線は追加しません。 第5章で使用した次の接続をそのまま使用します。
| 接続元 | 接続先 | 補足 |
|---|---|---|
Pico WのGP15 | 330Ω抵抗の片側 | GP15は物理ピン20 |
| 330Ω抵抗の反対側 | LEDのアノード | 一般に長い端子 |
| LEDのカソード | Pico WのGND | 物理ピン18を使用 |
図と配線手順は、 第5章の図5-1と5-4節を 参照してください。
この回路では、GP15がHIGHのときにLEDが点灯し、 LOWのときに消灯します。
11-4 Pico WでWebサーバーを起動する
Section titled “11-4 Pico WでWebサーバーを起動する”次のプログラムをch11_01_led_web_server.pyとして
PC側へ保存します。
実行前に、WIFI_SSIDとWIFI_PASSWORDの文字列を
講師から指定された値へ置き換えます。
引用符は残し、その内側だけを変更します。
コード全体は長く見えますが、
HTML_TEMPLATEはブラウザーへ表示する完成済みの画面定義です。
研修では全行を手入力せず、完成コードを使用します。
主に確認する部分は、Wi-Fi接続、リクエストの判定、
LED操作、レスポンス、停止時の後片付けです。
from machine import Pinimport networkimport socketimport time
WIFI_SSID = "YOUR_SSID"WIFI_PASSWORD = "YOUR_PASSWORD"CONNECT_TIMEOUT_MS = 20000LED_PIN = 15PORT = 80
HTML_TEMPLATE = """<!doctype html><html lang="ja"><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Pico W LED操作</title> <style> body { font-family: sans-serif; max-width: 34rem; margin: 3rem auto; padding: 0 1rem; text-align: center; color: #172554; } .panel { border: 2px solid #93c5fd; border-radius: 1rem; padding: 1.5rem; background: #eff6ff; } .state { font-size: 1.4rem; font-weight: bold; margin: 1.5rem 0; } form { display: inline-block; margin: 0.4rem; } button { min-width: 8rem; padding: 0.8rem 1rem; border: 0; border-radius: 0.6rem; color: white; font-size: 1rem; cursor: pointer; } .on { background: #15803d; } .off { background: #b91c1c; } </style></head><body> <main class="panel"> <h1>Pico W LED操作</h1> <p class="state">現在の状態:__LED_STATE__</p> <form action="/led/on" method="post"> <button class="on" type="submit">LEDを点灯</button> </form> <form action="/led/off" method="post"> <button class="off" type="submit">LEDを消灯</button> </form> </main></body></html>"""
def connect_wifi(): wlan = network.WLAN(network.WLAN.IF_STA)
wlan.active(False) time.sleep(1) wlan.active(True)
print("Wi-Fiへ接続します...") wlan.connect(WIFI_SSID, WIFI_PASSWORD)
start_time = time.ticks_ms()
while not wlan.isconnected(): status = wlan.status()
if status in ( network.STAT_WRONG_PASSWORD, network.STAT_NO_AP_FOUND, network.STAT_CONNECT_FAIL, ): wlan.active(False) raise RuntimeError( "Wi-Fi接続に失敗しました。status={}".format(status) )
elapsed = time.ticks_diff(time.ticks_ms(), start_time) if elapsed >= CONNECT_TIMEOUT_MS: wlan.active(False) raise RuntimeError( "Wi-Fi接続がタイムアウトしました。status={}".format(status) )
print("接続待ち... status={}".format(status)) time.sleep(1)
return wlan
def parse_request(request): try: request_line = request.decode("utf-8").split("\r\n", 1)[0] method, path, version = request_line.split() return method, path except (ValueError, UnicodeError): return "", ""
def make_page(led): if led.value(): state_text = "点灯" else: state_text = "消灯"
return HTML_TEMPLATE.replace("__LED_STATE__", state_text)
def send_page(client, led): body = make_page(led).encode("utf-8") header = ( "HTTP/1.1 200 OK\r\n" "Content-Type: text/html; charset=utf-8\r\n" "Content-Length: {}\r\n" "Connection: close\r\n" "\r\n" ).format(len(body)).encode("ascii")
client.sendall(header) client.sendall(body)
def send_redirect(client): client.sendall( b"HTTP/1.1 303 See Other\r\n" b"Location: /\r\n" b"Connection: close\r\n" b"\r\n" )
led = Pin(LED_PIN, Pin.OUT)led.off()server = Nonewlan = None
try: wlan = connect_wifi() ip_address = wlan.ifconfig()[0]
address = socket.getaddrinfo( "0.0.0.0", PORT, 0, socket.SOCK_STREAM )[0][-1] server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(address) server.listen(1)
print("Webサーバーを開始しました") print("ブラウザーで開くURL: http://{}/".format(ip_address))
while True: client = None
try: client, remote_address = server.accept() request = client.recv(1024) method, path = parse_request(request) print("リクエスト:", method, path)
if method == "POST" and path == "/led/on": led.on() send_redirect(client) elif method == "POST" and path == "/led/off": led.off() send_redirect(client) else: send_page(client, led) except OSError as error: print("HTTP処理エラー:", error) finally: if client is not None: client.close()except KeyboardInterrupt: print("Webサーバーを停止します")except Exception as error: print(error)finally: if server is not None: server.close() led.off() if wlan is not None: wlan.active(False)このコードは、1件ずつリクエストを処理する簡易構成です。 多数の利用者や長時間運用を目的としたものではありません。
11-5 ブラウザーからLEDを操作する
Section titled “11-5 ブラウザーからLEDを操作する”- PCとPico Wを同じ研修用Wi-Fiへ接続します
- Thonnyで
ch11_01_led_web_server.pyを実行します - Shellへ表示された
ブラウザーで開くURLを確認します - 表示されたURL全体を、PCのブラウザーのアドレス欄へ入力します
LEDを点灯ボタンを押し、外付けLEDと画面の状態を確認しますLEDを消灯ボタンを押し、外付けLEDと画面の状態を確認します- 確認後はThonnyの停止ボタンを押し、プログラムを終了します
Shellには、次のように表示されます。
Wi-Fiへ接続します...接続待ち... status=1Webサーバーを開始しましたブラウザーで開くURL: http://192.168.10.42/リクエスト: GET /リクエスト: POST /led/onリクエスト: GET /IPアドレスは表示例と異なります。 毎回、実行時にShellへ表示されたURLを使用してください。
http://を含む完全なURLを入力します。
ブラウザーが自動的にhttps://へ変更した場合は、
アドレス欄へhttp://から入力し直します。
11-6 コードの重要部分
Section titled “11-6 コードの重要部分”待ち受け用ソケットを作成する
Section titled “待ち受け用ソケットを作成する”address = socket.getaddrinfo( "0.0.0.0", PORT, 0, socket.SOCK_STREAM)[0][-1]server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server.bind(address)server.listen(1)socketは、ネットワーク通信の出入口を作るための機能です。
0.0.0.0は、Pico Wが持つネットワークインターフェースで
接続を受け付ける指定です。
PORT = 80は、HTTPで標準的に使用されるポート番号です。
ブラウザーからの接続を待つ
Section titled “ブラウザーからの接続を待つ”client, remote_address = server.accept()request = client.recv(1024)accept()は、ブラウザーから接続されるまで待ちます。
接続されると、recv()でHTTPリクエストを受け取ります。
この待ち時間中もプログラムは動作しています。 Shellへ新しい表示が出ないことだけで、 停止したと判断しないようにします。
リクエストの先頭行を読み取る
Section titled “リクエストの先頭行を読み取る”request_line = request.decode("utf-8").split("\r\n", 1)[0]method, path, version = request_line.split()HTTPリクエストの先頭行には、 メソッド、パス、HTTPの版が含まれます。
たとえば点灯ボタンを押した場合は、 次のような先頭行になります。
POST /led/on HTTP/1.1パスに応じてLEDを操作する
Section titled “パスに応じてLEDを操作する”if method == "POST" and path == "/led/on": led.on() send_redirect(client)elif method == "POST" and path == "/led/off": led.off() send_redirect(client)else: send_page(client, led)点灯または消灯のPOSTを受けたときだけ、
GP15の出力を変更します。
操作後はブラウザーを/へ戻します。
それ以外の要求には、現在のLED状態を含む画面を返します。
停止時に後片付けする
Section titled “停止時に後片付けする”finally: if server is not None: server.close() led.off() if wlan is not None: wlan.active(False)停止時やエラー時には、 待ち受け用ソケットを閉じ、LEDを消灯し、Wi-Fiを停止します。
実行をやり直すときは、 前のプログラムが停止していることを確認します。
11-7 ローカルWebサーバーとクラウド
Section titled “11-7 ローカルWebサーバーとクラウド”この章では、PCとPico Wが同じネットワーク内で直接通信します。 インターネット上のクラウドサービスは使用していません。
| 項目 | この章のローカルWebサーバー | 後続章のクラウド連携 |
|---|---|---|
| Pico Wの役割 | Webサーバー | クラウドへ接続するクライアント |
| 操作・閲覧する機器 | 同じネットワーク内のPC | インターネットへ接続したPCやスマートフォン |
| データの保存 | 基本的に行わない | クラウド側へ記録できる |
| 外出先からの利用 | 対象外 | サービスの仕組みにより可能 |
| 主な学習内容 | HTTPの要求と応答 | データ送信、蓄積、可視化、遠隔操作 |
Pico WをWebサーバーにすると、 端末から直接操作する仕組みを体験できます。
一方、離れた場所から状況を確認したり、 長期間のデータを保存したりするには、 クラウドを介する構成が適しています。
確認チェック
Section titled “確認チェック”- PCとPico Wを同じ研修用Wi-Fiへ接続できた
- 第5章のGP15外付けLED回路を確認した
- SSIDとパスワードをプレースホルダーから変更した
- Shellへ完全なURLが表示された
-
http://から始まるURLをPCのブラウザーで開けた - 点灯ボタンで外付けLEDが点灯した
- 消灯ボタンで外付けLEDが消灯した
- LEDの状態とブラウザーの表示が一致した
- 終了後にLEDが消灯した
- このWebサーバーをインターネットへ公開しない理由を説明できる
よくある問題と確認方法
Section titled “よくある問題と確認方法”| 状況 | 主な確認箇所 |
|---|---|
| URLを開いても画面が表示されない | プログラムが実行中か、完全なURL、http://、PCとPico Wの接続先 |
| 接続がタイムアウトする | PCとPico Wが同じネットワークか、端末間通信が許可されているか |
| 前回と同じURLで接続できない | 再接続後にShellへ表示された最新のIPアドレス |
| ブラウザーがHTTPSで開こうとする | アドレス欄へhttp://を含む完全なURLを入力 |
| LEDが変化しない | GP15、330Ω抵抗、LEDの向き、GND、Shellに表示されるメソッドとパス |
| 画面状態とLEDが一致しない | ページを再読み込みし、別のプログラムがGP15を操作していないか確認 |
| 再実行時にソケットのエラーが出る | 前のプログラムを停止し、必要に応じてPico Wを再起動 |
| ブラウザーから複数の要求が表示される | アイコンなどを取得する追加要求の場合がある。LED操作のPOSTを確認 |
IPアドレスを確認するときは、 固定値を本文やプログラムへ書き込むのではなく、 毎回Shellへ表示された値を使用します。
- Webブラウザーは要求を送るクライアント、Pico Wは応答するWebサーバーです
- HTTPリクエストに対し、WebサーバーはHTTPレスポンスを返します
GET /で操作画面を表示し、POSTでLEDの状態を変更しますsocketを使ってポート80で接続を待ち受けます- ブラウザーでは、Shellへ表示された
http://<IPアドレス>/を開きます - PCとPico Wは同じネットワークへ接続し、端末間通信が許可されている必要があります
- GP15の外付けLED回路は、第5章の配線をそのまま使用します
- この簡易Webサーバーは研修用であり、インターネットへ公開しません
- クラウドを使うと、データ保存や離れた場所からの確認へ発展できます
- MicroPython公式「socket – socket module」(v1.26.0)
- MicroPython公式「class WLAN – control built-in WiFi interfaces」(v1.26.0)
- Raspberry Pi公式「How to run a webserver on Raspberry Pi Pico W」
- Raspberry Pi公式「Raspberry Pi Pico-series Python SDK」(PDF)
次の第12章では、Pico Wから取得したデータを Googleスプレッドシートへ記録する仕組みを扱います。 ローカルネットワーク内の操作から、 クラウド上へのデータ送信と蓄積へ進みます。