ラズパイPicoW(Raspberry Pi Pico W)で ローカル Web サーバーを構築し、スマホやパソコンなどのクライアントから指示で、LEDを点灯、消灯させます。
実験準備
実験に必要な環境や部品を準備します。
機器
「ラズパイPicoWを始めよう。」記事で書きました、MicroPythonファームウェアをインストールしたラズパイPicoWと、統合開発環境ThonnyをインストールしたRaspi4Bを準備します。
使う部品
LED(発光ダイオード)、固定抵抗など実験に使う部品を準備します。
部 品 名 | 規 格 | 数 量 | 取扱い店(参考) |
LED(赤) | 3mm 赤色 | 1 | 電子工作ステーション |
LED(緑) | 3mm 緑色 | 1 | 電子工作ステーション |
ブレッドボード | BB-102 | 1 | 秋月電子通商 |
カーボン抵抗 | 1/4W 330Ω | 2 | 電子工作ステーション |
ジャンパーワイヤ | オス-メス (約20cm) | 1 | 電子工作ステーション |
配線
準備した機器と部品をつなぎ、MicroPythonでプログラミングを行って、ラズパイPicoW(Raspberry Pi Pico W)で ローカル Web サーバーを構築して、スマホやパソコンなどのクライアントから、Wi-Fi経由でアクセスし、LEDを点灯、消灯させます。
配線リスト
ラズパイPicoWのGPピンと抵抗を通して、LEDを接続します。
ラズパイPicoW | 部 品 | 部 品 |
GP26 | カーボン抵抗 R1 | LED(緑)アノード(足の長い方) |
GP27 | カーボン抵抗 R2 | LED(赤)アノード(足の長い方) |
GND | LED(緑)カソード(足の短い方) | ー |
GND | LED(赤)カソード(足の短い方) | ー |
配線図
スマホからLED点灯/消灯プログラムの実行
スマホのブラウザで、ローカル Web サーバーとして起動している、ラズパイPicoW(Raspberry Pi Pico W)のIPアドレスを指定し、ホームページを閲覧します。
表示された「緑と赤の点灯、消灯ボタン」をタップすると、該当するLEDが点灯、消灯し、ボタンの下にLEDの現在の状態が表示されます。
Raspi4BのThonnyを起動し、次のコードを「エディタ」に入力するか、リストをコピーしてペーストします。
##################################################
#
# モジュールの読み込み
#
##################################################
#GPIO Pin/ネットワーク
from machine import Pin
import network
import rp2
import time
#ソケット(Webサーバー)
import socket
##################################################
#
# LEDピン設定
#
##################################################
#緑のLED
led_grn = Pin(26, Pin.OUT)
#赤のLED
led_red = Pin(27, Pin.OUT)
#LED消灯
led_grn.value(0)
led_red.value(0)
##################################################
#
# WiFi接続パラメータ
#
##################################################
#WiFi接続パラメータ
ssid = "Wi-FiルータのSSIDを設定します。"
password = "Wi-Fiルータのパスワードを設定します。"
##################################################
#
# 【関数】クライエントへのHTML応答メッセージを作成
#
##################################################
def make_msg():
#HTMLメッセージを作成
html = """
<!DOCTYPE html>
<html>
<head>
<title>ラズパイPicoW Server</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.btnGreen {width: 100px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
background: #4CAF50;
color: #fff;
text-decoration: none;
border-radius: 5px;
font-size: 20px;
border: 2px solid #000000;
}
.btnRed {width: 100px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
background: #D11D53;
color: #fff;
text-decoration: none;
border-radius: 5px;
font-size: 20px;
border: 2px solid #000000;
}
.marg {
margin: auto;
}
.marg input{
margin: 10px;
}
</style>
</head>
<body><center>
<h2>ラズパイPicoW LED点灯/消灯</h2>
<table class="marg">
<tr>
<td>
<form action="./GRN_ON">
<input type="submit" class="btnGreen" value="点灯" />
</form>
</td>
<td>
<form action="./RED_ON">
<input type="submit" class="btnRed" value="点灯" />
</form>
</td>
</tr>
<tr>
<td>
<form action="./GRN_OFF">
<input type="submit" class="btnGreen" value="消灯" />
</form>
</td>
<td>
<form action="./RED_OFF">
<input type="submit" class="btnRed" value="消灯" />
</form>
</td>
</tr>
</table>
<p><font size="5">%s</font></p>
</center></body>
</html>
"""
return str(html)
##################################################
#
# 【関数】WiFiに接続
#
##################################################
def wifi_connect():
#WiFi地域(日本)の設定
rp2.country('JP')
#ステーションモードでの接続オブジェクト作成
wlan = network.WLAN(network.STA_IF)
#ステーションインタフェースの有効化
wlan.active(True)
#WiFiの省電力をオフに設定
wlan.config(pm = 0xa11140)
#接続状態確認
if not wlan.isconnected():
#WiFiに接続
wlan.connect(ssid, password)
try:
#IPアドレス取得待ち
while wlan.status() != network.STAT_GOT_IP:
print("接続中・・・")
time.sleep(1)
#IPアドレス取得
ip_add = wlan.ifconfig()[0]
print(f' {ip_add}に接続しました。')
#ラズパイPicoWのIPアドレスを返す
return ip_add
#強制中断
except KeyboardInterrupt:
print("「Ctrl + c」キーが押されました。")
machine.reset()
##################################################
#
# 【関数】WEBサーバソケット作成/接続受付開始
#
##################################################
def web_sock(ip_add):
#ラズパイPicoWのIPアドレス、HTTPポート(80)
web_adrs = (ip_add, 80)
#ソケット作成
sckt = socket.socket()
#ソケット/ポートをアドレスに関連付け
sckt.bind(web_adrs)
#接続受け付待ち
sckt.listen(1)
#ソケットを返す
return sckt
##################################################
#
# 【関数】サーバ/クライアント交信
#
##################################################
def exchange(web_cnnct):
#点灯/消灯状況
grn_sts = "緑:消灯"
red_sts = "赤:消灯"
led_sts = ""
#交信開始
while True:
try:
#データ受/送信ソケット、クライアントIPアドレス
clnt_conn, clnt_adrs = web_cnnct.accept()
#HTTPリクエストから最大1024バイトのデータを受信
request = clnt_conn.recv(1024)
#バイト文字列を文字列に変換
request = str(request)
try:
#HTTPリクエストの2番目の要素(URL)を抽出
request = request.split()[1]
except IndexError:
pass
#リクエスト処理(緑LED点灯)
if request == '/GRN_ON?':
led_grn.value(1)
grn_sts = "緑:点灯"
#リクエスト処理(赤LED点灯)
if request == '/RED_ON?':
led_red.value(1)
red_sts = "赤:点灯"
#リクエスト処理(緑LED消灯)
if request == '/GRN_OFF?':
led_grn.value(0)
grn_sts = "緑:消灯"
#リクエスト処理(赤LED消灯)
if request == '/RED_OFF?':
led_red.value(0)
red_sts = "赤:消灯"
#点灯/消灯状況
led_sts = grn_sts + " " + red_sts
#応答メッセージ
response = make_msg() % led_sts
#応答メッセージを送信
clnt_conn.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
clnt_conn.send(response)
#接続を終了させる
clnt_conn.close()
#接続に問題があった。
except OSError as e:
#接続を終了させる
clnt_conn.close()
##################################################
#
# メイン
#
##################################################
try:
#WiFiに接続
ip_add = wifi_connect()
#WEBサーバソケット作成/接続受付開始
web_cnnct = web_sock(ip_add)
#サーバ/クライアント交信
exchange(web_cnnct)
#強制中断
except KeyboardInterrupt:
print("「Ctrl + c」キーが押されました。")
machine.reset()
Thonnyの「F5」キーを押して、スマホからLED点灯/消灯プログラムを実行すると、Thonnyのシェルにネットワークへの接続メッセージが表示され、ラズパイPicoWのIPアドレスが表示されます。
スマホやパソコンのブラウザで、ラズパイPicoWのIPアドレスを指定し、ホームページを閲覧します。
表示された「緑と赤の点灯、消灯ボタン」をタップすると、該当するLEDが点灯、消灯し、ボタンの下にLEDの現在の状態が表示されます。
確認後、Thonnyの「Ctrl + c」キーを押すと、処理が中断しますので、中断後、「Ctrl + F2」キーを押して、プログラムを終了させます。
任意のファイル名でラズパイPicoWに保存します。(ここでは「3_11rmtled.py」で保存しました。)
スマホからLED点灯/消灯プログラムの仕組み
ラズパイPicoWをローカル Web サーバーとして起動し、スマホやパソコンのブラウザから、LEDを点灯、消灯させるプログラムの動きを簡単に見ていきます。
【関数】は決められた処理を実行し、その結果を返す命令で、メインや他の関数から呼び出され、利用されます。
モジュールの読み込み
ネットワークとの接続及び、Webサーバーを構築するためのモジュールをインポート(読み込み)ます。
#GPIO Pin/ネットワーク
from machine import Pin
import network
import rp2
import time
#ソケット(Webサーバー)
import socket
LEDピン設定
緑のLEDと赤のLEDを接続するGPピンの指定と、2つのLEDを消灯状態として初期化します。
#緑のLED
led_grn = Pin(26, Pin.OUT)
#赤のLED
led_red = Pin(27, Pin.OUT)
#LED消灯
led_grn.value(0)
led_red.value(0)
WiFi接続パラメータ
Wi-Fiルータに接続するための「SSID」、「パスワード」を設定します。
#WiFi接続パラメータ
ssid = "Wi-FiルータのSSIDを設定します。"
password = "Wi-Fiルータのパスワードを設定します。"
【関数】クライエントへのHTML応答メッセージを作成
スマホやパソコンブラウザから、Webサーバーにアクセスがあった時、関数の呼出し元にHTML応答メッセージを返します。
主なタグ | 説 明 |
<style>・・・</style> | 緑と赤のボタンのデザインを設定します。 |
<form action="./GRN_ON"> <input type="submit ・・・> </form>など | 設定したボタン(緑:点灯、緑:消灯、赤:点灯、:消灯、)がクリックされた時のURLを相対パスで指定します。 (例:192.168.0.xxx/GRN_ON) |
<p><font size="5">%s</font></p> | 「%s」を呼出し元から受け取った、LEDの点灯/消灯状況に置き換えます。 |
def make_msg():
#HTMLメッセージを作成
html = """
<!DOCTYPE html>
<html>
<head>
<title>ラズパイPicoW Server</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.btnGreen {width: 100px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
background: #4CAF50;
color: #fff;
text-decoration: none;
border-radius: 5px;
font-size: 20px;
border: 2px solid #000000;
}
・・・中略
</style>
</head>
<body><center>
<h2>ラズパイPicoW LED点灯/消灯</h2>
<table class="marg">
<tr>
<td>
<form action="./GRN_ON">
<input type="submit" class="btnGreen" value="点灯" />
</form>
</td>
・・・中略
</table>
<p><font size="5">%s</font></p>
</center></body>
</html>
"""
return str(html)
【関数】WiFiに接続
Wi-Fi接続パラメータで設定した「SSID」と「パスワード」を使って、Wi-Fiネットワークに接続し、正常に接続された場合は、関数の呼び出し元に、ラズパイPicoWのIPアドレスを返します。
接続の状況、IPアドレスをThonnyのシェルに表示します。
def wifi_connect():
#WiFi地域(日本)の設定
rp2.country('JP')
#ステーションモードでの接続オブジェクト作成
wlan = network.WLAN(network.STA_IF)
#ステーションインタフェースの有効化
wlan.active(True)
#WiFiの省電力をオフに設定
wlan.config(pm = 0xa11140)
#接続状態確認
if not wlan.isconnected():
#WiFiに接続
wlan.connect(ssid, password)
try:
#IPアドレス取得待ち
while wlan.status() != network.STAT_GOT_IP:
print("接続中・・・")
time.sleep(1)
#IPアドレス取得
ip_add = wlan.ifconfig()[0]
print(f' {ip_add}に接続しました。')
#ラズパイPicoWのIPアドレスを返す
return ip_add
#強制中断
except KeyboardInterrupt:
print("「Ctrl + c」キーが押されました。")
machine.reset()
【関数】WEBサーバソケットの作成と接続受付開始
ネットワークとの接続口であるソケット(socket)を作成します。
関数の呼出し元から受け取ったラズパイPicoWのIPアドレスと、WebサーバーのHTTPポート(送受信口)番号(80)の2つを、作成したソケットに結び付けます。(bind)
作成したソケットを接続受け付待ち(listen)にした後、関数の呼出し元に、関連付けされ、受付待ちになったソケットを返します。
def web_sock(ip_add):
#ラズパイPicoWのIPアドレス、HTTPポート(80)
web_adrs = (ip_add, 80)
#ソケット作成
sckt = socket.socket()
#ソケット/ポートをアドレスに関連付け
sckt.bind(web_adrs)
#接続受け付
sckt.listen(1)
#ソケットを返す
return sckt
【関数】サーバとクライアントの交信
LEDの点灯/消灯状況を初期化します。
関数の呼出し元から、WebサーバーのIPアドレスとHTTPポート番号(80)に結び付けされ、受付待ちになったソケットを受け取ります。
スマホやパソコン(クライアント)からの通信接続要求が来るまでプログラムを停止し、通信接続要求を受取った時、プログラムを再開します。(accept)
接続後、スマホやパソコンからのメッセージの内、フォーム(<form action="./GRN_ON">)に対応した処理(LEDの点灯/消灯など)を実行します。
LEDの点灯、消灯の現状をHTMLの「%s」と置き換え、スマホやパソコンに応答した後、接続を終了します。
def exchange(web_cnnct):
#点灯/消灯状況
grn_sts = "緑:消灯"
red_sts = "赤:消灯"
led_sts = ""
#交信開始
while True:
try:
#データ受信/送信ソケット、クライアントIPアドレス
clnt_conn, clnt_adrs = web_cnnct.accept()
#HTTPリクエストから最大1024バイトのデータを受信
request = clnt_conn.recv(1024)
#バイト文字列を文字列に変換
request = str(request)
try:
#HTTPリクエストの2番目の要素(URL)を抽出
request = request.split()[1]
except IndexError:
pass
#リクエスト処理(緑LED点灯)
if request == '/GRN_ON?':
led_grn.value(1)
grn_sts = "緑:点灯"
#リクエスト処理(赤LED点灯)
if request == '/RED_ON?':
led_red.value(1)
red_sts = "赤:点灯"
#リクエスト処理(緑LED消灯)
if request == '/GRN_OFF?':
led_grn.value(0)
grn_sts = "緑:消灯"
#リクエスト処理(赤LED消灯)
if request == '/RED_OFF?':
led_red.value(0)
red_sts = "赤:消灯"
#点灯/消灯状況
led_sts = grn_sts + " " + red_sts
#応答メッセージ
response = make_msg() % led_sts
#応答メッセージを送信
clnt_conn.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
clnt_conn.send(response)
#接続を終了させる
clnt_conn.close()
#接続に問題があった。
except OSError as e:
#接続を終了させる
clnt_conn.close()
メイン
作成した関数を順次呼び出して、Webサーバーを構築します。
Webサーバーはスマホやパソコンからの接続要求を処理して、LEDの点灯、消灯させ、LEDの現状を接続要求元にメッセージとして応答します。
「Ctrl + c」キーを押すと、ラズパイPicoWをリセットします。
まとめ
ラズパイPicoW(Raspberry Pi Pico W)で ローカル Web サーバーを構築し、スマホやパソコンなどのクライアントから指示で、LEDを点灯、消灯させました。
Wi-Fiを使ったネットワークへの接続方法、「socket」、「bind」、「listen」の機能を使った ローカル Web サーバーの構築方法を学びました。
スマホやパソコンなどのクライアントから、 Web サーバーに要求があった場合、「accept」の機能を使って、受信したメッセージに対応した処理を行う方法を学びました。