.. note::

    こんにちは、SunFounderのRaspberry Pi & Arduino & ESP32愛好家コミュニティへようこそ！Facebook上でRaspberry Pi、Arduino、ESP32についてもっと深く掘り下げ、他の愛好家と交流しましょう。

    **参加する理由は？**

    - **エキスパートサポート**：コミュニティやチームの助けを借りて、販売後の問題や技術的な課題を解決します。
    - **学び＆共有**：ヒントやチュートリアルを交換してスキルを向上させましょう。
    - **独占的なプレビュー**：新製品の発表や先行プレビューに早期アクセスしましょう。
    - **特別割引**：最新製品の独占割引をお楽しみください。
    - **祭りのプロモーションとギフト**：ギフトや祝日のプロモーションに参加しましょう。

    👉 私たちと一緒に探索し、創造する準備はできていますか？[|link_sf_facebook|]をクリックして今すぐ参加しましょう！

7. ウェブサーバーのセットアップ
====================================

この記事では、Pico Wをブラウザから回路を操作したり、センサーからの読み取りを取得できるウェブサーバーにする方法を学びます。

|setup_web|

**1. 必要なコンポーネント**

このプロジェクトには、以下のコンポーネントが必要です。

一式を購入すると便利です。こちらがリンクです：

.. list-table::
    :widths: 20 20 20
    :header-rows: 1

    *   - 名前	
        - このキットに含まれるアイテム
        - リンク
    *   - ケプラーキット	
        - 450+
        - |link_kepler_kit|

以下のリンクから個別に購入することもできます。

.. list-table::
    :widths: 5 20 5 20
    :header-rows: 1

    *   - SN
        - コンポーネント	
        - 数量
        - リンク

    *   - 1
        - :ref:`cpn_pico_w`
        - 1
        - |link_picow_buy|
    *   - 2
        - マイクロUSBケーブル
        - 1
        - 
    *   - 3
        - :ref:`cpn_breadboard`
        - 1
        - |link_breadboard_buy|
    *   - 4
        - :ref:`cpn_wire`
        - 数本
        - |link_wires_buy|
    *   - 5
        - :ref:`cpn_resistor`
        - 4(1-330Ω, 2-220Ω, 1-10KΩ)
        - |link_resistor_buy|
    *   - 6
        - :ref:`cpn_rgb`
        - 1
        - |link_rgb_led_buy|
    *   - 7
        - :ref:`cpn_thermistor`
        - 1
        - |link_thermistor_buy|
    *   - 8
        - :ref:`cpn_lipo_charger`
        - 1
        -  
    *   - 9
        - 18650バッテリー
        - 1
        -  
    *   - 10
        - バッテリーホルダー
        - 1
        -  

**2. 回路を組み立てる**

    .. warning:: 
        
        図に示されている通り、Li-poチャージャーモジュールが接続されていることを確認してください。そうでないと、ショート回路が起きてバッテリーや回路が損傷する可能性があります。

.. image:: img/wiring/7.web_page_bb.png
    :width: 800



**3. コードを実行する**

#. ``kepler-kit-main/iot`` のパスの下にある ``7_web_page.py`` ファイルを開きます。
#. **Run current script** ボタンをクリックするか、F5キーを押して実行します。接続が成功すると、Pico WのIPが表示されます。

    .. image:: img/7_web_server.png

    .. note::

        コードを実行する前に、Pico Wに ``do_connect.py`` と ``secrets.py`` スクリプトがあることを確認してください。もしなければ、 :ref:`iot_access` を参照して作成してください。

#. Pico WのIPアドレスをブラウザに入力して、このプロジェクトのために構築されたウェブページにアクセスします。任意のボタンをクリックして、RGB LEDの色を変更し、温度と湿度を更新します。

    .. image:: img/web-1.png
        :width: 500

#. このスクリプトを起動できるようにしたい場合は、Raspberry Pi Pico Wに ``main.py`` として保存できます。

**動作原理は？**

アクセスしているウェブページは実際には何らかのサーバーでホストされており、そのサーバーのソケットが訪問時にウェブページを送信します。
ソケットとは、サーバーが接続を希望するクライアントを待ち受ける方法です。

このプロジェクトでは、Pico Wがサーバーであり、ブラウザを介してPico Wでホストされているウェブページにアクセスしています。

最初にソケットを作成します。これにはIPアドレスと |link_port| が必要です。
ネットワーク接続とIPの取得方法は、 :ref:`iot_access` で説明されています。ポートには80を使用します。
ソケットの設定が完了したら、それを返して次のステップで使用します。

`socket library - Python Docs <https://docs.python.org/3/library/socket.html>`_ 

.. code-block:: python

    import socket

    def open_socket(ip):
        # ソケットを開く
        address = (ip, 80)
        connection = socket.socket()
        connection.bind(address)
        connection.listen(1)
        print(connection)
        return(connection)

次に、以前に設定したソケットを使用してウェブサービスを設定します。
以下のコードにより、Pico Wはブラウザからのアクセス要求を受け取ることができます。

.. code-block:: python

    def serve(connection):
        while True:
            client = connection.accept()[0]
            request = client.recv(1024)
            client.close()

次に、訪問者に送信するhtmlページが必要です。この例では、変数 ``html`` に文字形式で単純なhtmlページを格納しています。

.. note:: 
    自分でhtmlを書きたい場合は、 |link_html| でヘルプを得ることができます。

.. code-block:: python

    def webpage(value):
        html = f"""
                <!DOCTYPE html>
                <html>
                <body>
                <form action="./red">
                <input type="submit" value="red " />
                </form>
                <form action="./green">
                <input type="submit" value="green" />
                </form>
                <form action="./blue">
                <input type="submit" value="blue" />
                </form>
                <form action="./off">
                <input type="submit" value="off" />
                </form>
                <p>温度は{value}度です</p>
                </body>
                </html>
                """
        return html


訪問者にHTMLページを送信する。

.. code-block:: python
    :emphasize-lines: 5,6

    def serve(connection):
        while True:
            client = connection.accept()[0]
            request = client.recv(1024)
            html = webpage(0)
            client.send(html)
            client.close()

上記の部分を組み合わせると、ブラウザでページにアクセスできます。効果を確認したい場合は、以下のコードをThonnyで実行してください。

.. code-block:: python

    import machine
    import socket

    from secrets import *
    from do_connect import *

    def webpage(value):
        html = f"""
                <!DOCTYPE html>
                <html>
                <body>
                <form action="./red">
                <input type="submit" value="赤" />
                </form>
                <form action="./green">
                <input type="submit" value="緑" />
                </form>
                <form action="./blue">
                <input type="submit" value="青" />
                </form>
                <form action="./off">
                <input type="submit" value="オフ" />
                </form>
                <p>温度は{value}度です</p>
                </body>
                </html>
                """
        return html

    def open_socket(ip):
        # ソケットを開く
        address = (ip, 80)
        connection = socket.socket()
        connection.bind(address)
        connection.listen(1)
        print(connection)
        return(connection)

    def serve(connection):
        while True:
            client = connection.accept()[0]
            request = client.recv(1024)
            html = webpage(0)
            client.send(html)
            client.close()

    try:
        ip = do_connect()
        if ip is not None:
            connection = open_socket(ip)
            serve(connection)
    except KeyboardInterrupt:
        machine.reset()

上記のコードを実行すると、ウェブページのみが表示され、RGB LEDの制御やセンサーの読み取りは許可されていないことがわかります。
このウェブサービスはさらに洗練される必要があります。

最初に知るべきことは、ブラウザがウェブページにアクセスしたときにサーバーが受け取る情報です。それゆえに、 ``serve()`` をわずかに変更して ``request`` を出力します。

.. code-block:: python
    :emphasize-lines: 5,6

    def serve(connection):
        while True:
            client = connection.accept()[0]
            request = client.recv(1024)
            request = str(request)
            print(request)
            html = webpage(0)
            client.send(html)
            client.close()

スクリプトを再実行すると、シェルはウェブページでキーを押すときに以下のメッセージを出力します。

.. code-block:: 

    b'GET /red? HTTP/1.1\r\nHost: 192.168.18.162\r\nConnection: keep-alive.......q=0.5\r\n\r\n'
    b'GET /favicon.ico HTTP/1.1\r\nHost: 192.168.18.162\r\nConnection: keep-alive.......q=0.5\r\n\r\n'
    b'GET /blue? HTTP/1.1\r\nHost: 192.168.18.162\r\nConnection: keep-alive.......q=0.5\r\n\r\n'
    b'GET /favicon.ico HTTP/1.1\r\nHost: 192.168.18.162\r\nConnection: keep-alive.......q=0.5\r\n\r\n'

読むには長すぎます！


しかし、実際に必要なのは ``/red?`` や ``/blue?`` の前にある小さな情報だけです。
これはどのボタンが押されたのかを教えてくれます。それで、キーストロークの情報を抽出するために ``serve()`` を少し改良しました。

.. code-block:: python
    :emphasize-lines: 6,7,8,9

    def serve(connection):
        while True:
            client = connection.accept()[0]
            request = client.recv(1024)
            request = str(request)
            try:
                request = request.split()[1]
            except IndexError:
                pass
            print(request)  
            html = webpage(0)
            client.send(html)
            client.close()

プログラムを再実行すると、ウェブページでキーを押すと、シェルは以下のようなメッセージを出力します。

.. code-block:: 

    /red?
    /favicon.ico
    /blue?
    /favicon.ico
    /off?
    /favicon.ico

次に、 ``request`` の値に応じてRGB LEDの色を変更するだけです。

.. code-block:: python

    def serve(connection):
        while True:
            client = connection.accept()[0]
            request = client.recv(1024)
            request = str(request)
            try:
                request = request.split()[1]
            except IndexError:
                pass
            
            print(request)
            
            if request == '/off?':
                red.low()
                green.low()
                blue.low()
            elif request == '/red?':
                red.high()
                green.low()
                blue.low()
            elif request == '/green?':
                red.low()
                green.high()
                blue.low()
            elif request == '/blue?':
                red.low()
                green.low()
                blue.high()
            
            html = webpage(0)
            client.send(html)
            client.close()

最後に、ウェブページにサーミスターの値を表示する必要があります（サーミスターの使用方法の詳細については、 :ref:`py_temp` を参照してください）。
この部分は実際にはHTMLのテキストを修正することで実現されます。
``webpage(value)`` 関数でパラメータを設定し、入力パラメータを変更するだけでウェブページに表示される数字を変更します。

.. code-block:: python
    :emphasize-lines: 30,31

    def serve(connection):
        while True:
            client = connection.accept()[0]
            request = client.recv(1024)
            request = str(request)
            try:
                request = request.split()[1]
            except IndexError:
                pass
            
            #print(request)
            
            if request == '/off?':
                red.low()
                green.low()
                blue.low()
            elif request == '/red?':
                red.high()
                green.low()
                blue.low()
            elif request == '/green?':
                red.low()
                green.high()
                blue.low()
            elif request == '/blue?':
                red.low()
                green.low()
                blue.high()

            value = '%.2f' % temperature()    
            html = webpage(value)
            client.send(html)
            client.close()

.. https://projects.raspberrypi.org/en/projects/get-started-pico-w/3
