Webサーバー構築、スマホからLED点灯/消灯。

2024年9月16日

【 当サイトには広告リンクが含まれています。 】


ラズパイPicoW(Raspberry Pi Pico W)で ローカル Web サーバーを構築し、スマホやパソコンなどのクライアントから指示で、LEDを点灯、消灯させます。


実験準備

実験に必要な環境や部品を準備します。

機器

「ラズパイPicoWを始めよう。」記事で書きました、MicroPythonファームウェアをインストールしたラズパイPicoWと、統合開発環境ThonnyをインストールしたRaspi4Bを準備します。


使う部品

LED(発光ダイオード)、固定抵抗など実験に使う部品を準備します。

部 品 名規 格数 量取扱い店(参考)
LED(赤)3mm 赤色1電子工作ステーション
LED(緑)3mm 緑色1電子工作ステーション
ブレッドボードBB-1021秋月電子通商
カーボン抵抗1/4W 330Ω2電子工作ステーション
ジャンパーワイヤオス-メス
(約20cm)
1電子工作ステーション


配線

準備した機器と部品をつなぎ、MicroPythonでプログラミングを行って、ラズパイPicoW(Raspberry Pi Pico W)で ローカル Web サーバーを構築して、スマホやパソコンなどのクライアントから、Wi-Fi経由でアクセスし、LEDを点灯、消灯させます。

配線リスト

ラズパイPicoWのGPピンと抵抗を通して、LEDを接続します。

ラズパイPicoW部 品部 品
GP26カーボン抵抗 R1LED(緑)アノード(足の長い方)
GP27カーボン抵抗 R2LED(赤)アノード(足の長い方)
GNDLED(緑)カソード(足の短い方)
GNDLED(赤)カソード(足の短い方)


配線図



スマホから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」の機能を使って、受信したメッセージに対応した処理を行う方法を学びました。