Wi-Fiアクセスポイント(AP)モードで使用してみた。(Raspberry Pi Pico W)

つぶやき

アクセスポイント(AP)の設置

APモードでは、ネットワークルーターは不要です。Raspi Pico W自身をアクセスポイントとして設定することで、Wi-Fi対応デバイス(スマートフォン、ノートPCなど)を、ルーターを介さずにRaspi Pico Wに接続できるようになります。 つまり、Wi-Fiのない環境でもブラウザ操作でRaspi Pico 制御すること(LEDの点灯やセンサー値の確認など)ができる機能です。

import network
import gc
import time

# スマートフォンやパソコンからこのアクセスポイントに接続できます。
# 接続後、出力に表示されたIPアドレスを使ってデバイスにアクセスできます。
SSID: str = "Raspi_Pico_W"
PASSWORD: str = "qwertyuiop"

gc.collect()  # メモリを解放するためにガベージコレクションを実行

# アクセスポイントのインターフェースを作成
ap = network.WLAN(network.AP_IF)

# secrets.py に定義されたSSIDとパスワードでアクセスポイントを設定
ap.config(essid=SSID, password=PASSWORD)

# 静的IPアドレスを設定:(IPアドレス, サブネットマスク, ゲートウェイ, DNS)
ap.ifconfig(("192.168.4.1", "255.255.255.0", "192.168.4.1", "192.168.4.2"))

# アクセスポイントを有効化して、他のデバイスから見えるようにする
ap.active(True)

# ネットワーク設定を表示
print("アクセスポイントの準備ができました。以下の情報で接続してください:")
print("IPアドレス: {}\nサブネットマスク: {}\nゲートウェイ: {}\nDNS: {}".format(*ap.ifconfig()))

# アクセスポイントを維持するためにスクリプトを動作させ続ける
while True:
    time.sleep(1)  # CPU使用率が高くならないようにスリープ

APでボードの温度を確認する。

# このスクリプトは Raspberry Pi Pico W を Wi-Fi アクセスポイント(AP)として動作させ、
# 内蔵温度センサーの値を Web ブラウザに表示する簡単な HTTP サーバーを起動します。

import network     # Wi-Fi 通信を行うためのモジュール
import socket      # ソケット通信(HTTPサーバー)用モジュール
import gc          # メモリ解放用のガベージコレクションモジュール
import time        # 遅延処理用
from machine import ADC  # Pico W のアナログセンサー読み取り用(温度センサー)

# ====== Wi-Fi アクセスポイントの設定 ======
SSID = "Raspi_Pico_W"
PASSWORD = "qwertyuiop"

gc.collect()  # メモリを少しでも確保するためにガベージコレクションを実行

# アクセスポイントモードでWi-Fiインターフェースを作成
ap = network.WLAN(network.AP_IF)

# アクセスポイントとしてのSSIDとパスワードを設定
ap.config(essid=SSID, password=PASSWORD)

# アクセスポイントのIPアドレスなどを手動で設定(固定IP)
ap.ifconfig(("192.168.4.1", "255.255.255.0", "192.168.4.1", "192.168.4.2"))

# アクセスポイントを有効化(他の端末から見えるようにする)
ap.active(True)

# コンソールにネットワーク設定を表示
print("Access Point is ready. Connect from your device:")
print("IP Address: {}\nNet Mask: {}\nGateway: {}\nDNS: {}".format(*ap.ifconfig()))

# ====== 内蔵温度センサーの設定 ======
# 内蔵温度センサーは ADC(4) に接続されている
sensor_temp = ADC(4)
# ADCの読み取り値(0~65535)を電圧(0~3.3V)に変換する係数
conversion_factor = 3.3 / 65535

# 温度を読み取り摂氏に変換する関数
def read_temperature():
    ### ADC から現在の電圧を読み取る
    reading = sensor_temp.read_u16() * conversion_factor  # ADC値を電圧に変換
    ### RP2040 データシートに基づく温度換算式(摂氏温度に変換)
    temperature_c = 27 - (reading - 0.706) / 0.001721
    return round(temperature_c, 1)  # 小数第1位まで丸めて返す

# 温度履歴のリスト (最大 20 件)
history = []

# ====== HTTP サーバーの設定 ======
# サーバーのIPアドレスとポート番号を取得(ポート80でリッスン)
addr = socket.getaddrinfo("0.0.0.0", 80)[0][-1]

# ソケットを作成し、接続要求を受け付けられるようにする
server_socket = socket.socket()
server_socket.bind(addr)     # ソケットをアドレスにバインド
server_socket.listen(1)      # 同時接続は1台まで受け付け

print("HTTP server running at http://192.168.4.1")

# ====== メインループ:温度を表示するWebページを提供 ======
while True:
    ### 現在の温度を取得
    temperature = read_temperature()
    ### 履歴に温度を追加
    history.append(temperature)
    ### 履歴が20件を超えた場合、古いデータを削除
    if len(history) > 20:
        history.pop(0)  # 最新20件だけ残す

    try:
        ### クライアントからの接続要求を待つ
        client, addr = server_socket.accept()
        print("Client connected from", addr)
        request = client.recv(1024)  # クライアントからのリクエストを受信

        ### 温度履歴を JavaScript の配列形式に変換
        js_data = ','.join(str(t) for t in history)

        ### クライアントに返す HTML を作成
        html = f"""\
HTTP/1.1 200 OK

<!DOCTYPE html>
<html>
<head>
    <title>Pico Temperature History</title>
    <meta http-equiv="refresh" content="5">
    <style>
        body {{
            font-family: sans-serif;
            text-align: center;
            margin-top: 40px;
        }}
        canvas {{
            border: 1px solid #ccc;
        }}
    </style>
</head>
<body>
    <h1>Temperature History</h1>
    <p>Current Temperature: <strong>{temperature:.1f} degree C</strong></p>
    <canvas id="chart" width="300" height="150"></canvas>
    <script>
        // JavaScript でグラフ描画を行う部分
        const data = [{js_data}];
        const canvas = document.getElementById('chart');
        const ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.beginPath();
        ctx.strokeStyle = 'blue';

        let max = Math.max(...data);
        let min = Math.min(...data);
        let range = max - min || 1;
        let scaleX = canvas.width / (data.length - 1);
        let scaleY = canvas.height / range;

        for (let i = 0; i < data.length; i++) {{
            let x = i * scaleX;
            let y = canvas.height - (data[i] - min) * scaleY;
            if (i === 0) {{
                ctx.moveTo(x, y);
            }} else {{
                ctx.lineTo(x, y);
            }}
        }}
        ctx.stroke();
    </script>
    <p>(Graph updates every 5 seconds)</p>
</body>
</html>
"""
        ### クライアントに HTML を送信
        client.send(html)
        client.close()

    except Exception as e:
        ### エラーが発生した場合の処理
        print("Error:", e)
        client.close()

APで本体LEDを点滅を操作する。

import network
import socket
import gc
import machine

# ====== Wi-Fi アクセスポイント設定 ======
SSID = "Raspi_Pico_W"
PASSWORD = "qwertyuiop"

gc.collect()

ap = network.WLAN(network.AP_IF)
ap.config(essid=SSID, password=PASSWORD)
ap.ifconfig(("192.168.4.1", "255.255.255.0", "192.168.4.1", "192.168.4.2"))
ap.active(True)

print("Access Point is ready. Connect from your device:")
print("IP Address: {}\nNet Mask: {}\nGateway: {}\nDNS: {}".format(*ap.ifconfig()))

# HTMLでウェブページを作成
html = """
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    html {
      font-family: Helvetica;
      text-align: center;
    }
    button {
      color: white;
      padding: 15px 32px;
      font-size: 18px;
      margin: 3px 1px;
      cursor: pointer;
      border: none;
      border-radius: 5px;
    }
    .green { background-color: #00BFFF; }
    .red { background-color: #f44336; }
    button, form { display: inline-block; text-align: center; }
  </style>
</head>
<body style="%s">
  <h2>LED Control</h2>
  <form>
    <button class="green" name="led" value="on" type="submit">LED ON</button>
    <button class="red" name="led" value="off" type="submit">LED OFF</button>
  </form>
  <p>%s</p>
</body>
</html>
"""

# LEDのピンを設定
led = machine.Pin("LED", machine.Pin.OUT)

# LEDの状態を初期化
ledState = "LED State Unknown"

# ソケットの設定を開始
addr = socket.getaddrinfo("0.0.0.0", 80)[0][-1]
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(1)

# クライアントからの接続を待つ
while True:
    try:
        # クライアントからの接続を受け付ける
        cl, addr = s.accept()
        print("client connected from", addr)
        # クライアントからのリクエストを受け取る
        request = cl.recv(1024).decode()

        # LED制御
        if "?led=on" in request:
            led.on()
            print("LED ON")
        elif "?led=off" in request:
            led.off()
            print("LED OFF")


        # LEDの現在の状態を設定
       # ledState = "LED is OFF" if led.value() == 0 else "LED is ON"
        if led.value() == 0:
            ledState = "LED is OFF"
            bgColor = "background-color: #FFCCCC;"  # LED OFFなら薄赤
        else:
            ledState = "LED is ON"
            bgColor = "background-color: #CCFFCC;"  # LED ONなら薄緑  
        

        # レスポンスを作成し、クライアントに送信       
        response = "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n" + (html % (bgColor, ledState))
        cl.send(response)
        cl.close()

    # エラーが発生した場合、クライアントとの接続を閉じる
    except OSError as e:
        cl.close()
        print("Connection closed. Error:", e)

コメント