0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Raspberry Pi Pico 2 W の Wi‑Fi で Telnet / HTTP LED 制御を作る(MicroPython)

0
Posted at

はじめに

RaspberryPi Pico 2 W のWifi機能を使って、Telnet / HTTP のプロトコルから LED を制御するMicro Pythonのソフトを作ってみた。

目次

RaspberryPi Pico 2 W の準備

Raspberry Pi Pico 2 W で MicroPython を使うには、まず UF2 ファイルを書き込む必要がある。以下の公式サイトから MicroPython の UF2 をダウンロードし、Pico に書き込む。

  • Pico 2 W の BOOTSEL ボタンを押しながら PC と USB 接続する
  • PC に RPI-RP2 というドライブが表示される
  • ダウンロードした UF2 ファイルを ドラッグ&ドロップする
  • 自動的に再起動して MicroPython が使える状態になる
    その後、Terminal からシリアル接続すれば、すぐに REPL が使える。

REPL
MicroPython v1.28.0 on 2026-04-06; Raspberry Pi Pico 2 W with RP2350
Type "help()" for more information.
>>>
>>> print("hello")
hello
>>>

戻る

Pythonコードの書き込み方法

Raspberry Pi Pico 2 W に MicroPython のコードを書き込む方法はいくつかある。Thonny、rshell、mpremote 等が使える。
本記事ではmpremoteの使い方についてまとめる。 mpremoteは、REPL、ファイルコピー、リセット実行など一通りの操作が可能。

インストール

pip install mpremote

Pico 2W のポート番号(COMx)を指定して繋げる。

COM番号は自動で設定されるので、通常はconnect COMxは不要だが、COM番号がずれたりしている場合は直接指定する。以降説明するコマンドについても同様。

cmd
$ mpremote connect COMx
---
Connected to MicroPython at COMx
Use Ctrl-] or Ctrl-x to exit this shell

>>>
---

Pythonコードの書き込み

cp PC⇔Pico 2 W へコピー(:/ がroot)

cmd
# PC → pico
$ mpremote cp ./main.py :/

# pico → PC
$ mpremote cp :/main.py ./

その他いろいろ

cmd
$ mpremote ls
$ mpremote tree
$ mpremote cat :main.py
$ mpremote reset 
$ mpremote mkdir :/dir
$ mpremote rm :/dir
$ mpremote touch :test.txt

エディタ

PCの環境変数EDITORに設定されたエディターで、Pico 2 W のファイルを直接編集できる。これがとても便利。編集後、notepadを閉じれば自動的にpico 2 w へ保存される。

  1. Windowsの場合は環境変数に notepad.exe を設定
    環境変数はPCを再起動しないと反映されないので注意。
    image.png
     
  2. editor起動し、編集・保存。
$ mpremote edit :main.py

戻る

Wi‑Fi 接続

Wi‑Fi 接続のコードは下記の通り。接続するSSIDがわかっているのであれば、
スキャンは不要。Pico 2 W は 5GHz Wi‑Fi 非対応で、認証方式の相性にも注意が必要。接続できない場合はまずスキャン結果を確認すると原因がわかりやすい。

No. 処理内容
1.スキャン
 def wifi_scan()
スキャンでssidや電波強度、認証の種類を取得できる。
2.Wi‑Fiに接続
 def wifi_connect()
ssid, passwordを設定して接続し、wlan.isconnected()で接続結果待ちをする。
4.接続結果
 def wifi_status()
IPアドレスやMACアドレスを取得できる。
wlan.ifconfig()
wlan.config('mac')

networkのドキュメントは下記。本記事ではステーションモードを使っているがアクセスポイントモードとしても動作させることができる。Pico 2Wとローカルのネットワークを構築するときに使えそう。

main.py
import network
import time

# 1.スキャン
def wifi_scan():
  # スキャン
  wlan = network.WLAN(network.STA_IF)
  wlan.active(True)
  nets = wlan.scan()

  # リスト化
  ssid_list=[]
  auth_dict = {0:"OPEN", 1:"WEP", 2:"WPA-PSK", 3:"WPA2-PSK", 4:"WPA/WPA2-PSK", 5:"WPA2-Enterprise", 6:"WPA3-PSK", 7:"WPA2/WPA3-PSK"}

  for ssid, bssid, channel, rssi, auth, hidden in nets:
      entry = {
          "ssid": ssid.decode(),
          "bssid": ":".join(f"{b:02x}" for b in bssid),
          "channel": channel,
          "rssi": rssi,
          "auth": auth_dict.get(auth, "unknown"),
          "hidden": hidden,
      }
      ssid_list.append(entry)
  ssid_list.sort(key=lambda x: x["rssi"], reverse=True)	#電界強度強い順にソート

  return ssid_list
        
# 2.Wi‑Fi に接続
def wifi_connect(ssid, password):
  wlan = network.WLAN(network.STA_IF)
  wlan.active(True)
  wlan.connect(ssid, password)

  while not wlan.isconnected():
      print('Connecting...',ssid)
      time.sleep(1)
  return wlan
  
# 3.接続結果
def wifi_status(wlan):
  ip, subnet, gateway, dns = wlan.ifconfig()
  mac = ":".join(f"{b:02x}" for b in wlan.config('mac'))
  status = {'ip':ip, 'subnet':subnet, 'gateway':gateway, 'dns':dns, 'mac':mac}
  return status

# ------------------------
# main()
# ------------------------
def main():
  # スキャン
  print(wifi_scan())

  # 接続
  ssid='your_SSID'
  password='your_PASSWORD'
  wlan = wifi_connect(ssid, password)

  # 結果
  print(wifi_status(wlan))
  
if __name__ == "__main__":
    main()

戻る

Telnetアプリ

MicroPython には Linux のような telnetd(Telnet デーモン)が存在しないため、簡易的なTelnetの双方向通信アプリを作る。asyncio を使って送信・受信を並列処理する Telnetアプリは下記の通り。このソフトをPico 2 W に書き込んで起動すると、TeraTerm等でTelnetでアクセスしてLEDの制御などができる。

192.168.0.100:23 へTeraTermでアクセス
image.png

関数 処理内容
wifi_connect_with_ip() Wi‑Fi に接続する関数。固定 IP を使いたい場合は wlan.ifconfig((ip, mask, gw, dns))`` を指定することで DHCP を使わずに IP を設定できる。
wlan.ifconfig((ip,mask,gw,dns)) (IPアドレス, サブネットマスク, ゲートウェイ, DNS) の順で指定する。例:("192.168.0.50","255.255.255.0","192.168.0.1","192.168.0.1")``
asyncio.start_server(handle_client, "0.0.0.0", 23) Telnet のポート番号 23 でクライアント接続待ちを行う。クライアントが接続すると handle_client() が呼ばれる。
handle_client(reader, writer) Telnet クライアントが接続すると呼ばれる。内部で 2つのタスクtask_status():LED状態通知、task_receiver():コマンド受信)を同時実行する。
task_status() 5秒ごとに呼ばれ、Pico 2 W の LED 状態をクライアントへ通知する。writer.write() で送信データをセットし、writer.drain() で送信する。
task_receiver() クライアントからの入力を受信し、コマンドに応じて処理を行う。LED制御・Wi‑Fi情報表示・SSID一覧などを実行する。
task_receiver() のコマンド一覧 help :helpを表示
led on:LED を ON にする
led off:LED を OFF にする
wifi : Wi‑Fi ステータスを表示
ssid :周囲の SSID 一覧を表示
main.py
import network
import time
import asyncio
from machine import Pin

led = Pin("LED", Pin.OUT)

# ----------------
# スキャン
# ----------------
def wifi_scan():
  # スキャン
  wlan = network.WLAN(network.STA_IF)
  wlan.active(True)
  nets = wlan.scan()

  # リスト化
  ssid_list=[]
  auth_dict = {0:"OPEN", 1:"WEP", 2:"WPA-PSK", 3:"WPA2-PSK", 4:"WPA/WPA2-PSK", 5:"WPA2-Enterprise", 6:"WPA3-PSK", 7:"WPA2/WPA3-PSK"}

  for ssid, bssid, channel, rssi, auth, hidden in nets:
      entry = {
          "ssid": ssid.decode(),
          "bssid": ":".join(f"{b:02x}" for b in bssid),
          "channel": channel,
          "rssi": rssi,
          "auth": auth_dict.get(auth, "unknown"),
          "hidden": hidden,
      }
      ssid_list.append(entry)
  ssid_list.sort(key=lambda x: x["rssi"], reverse=True)	#電界強度強い順にソート

  return ssid_list
        
# ----------------
# Wi-Fi接続
# ----------------
def wifi_connect_with_ip(ssid, password, ip='', mask='255.255.255.0', gw='192.168.0.1', dns='192.168.0.1'):
  wlan = network.WLAN(network.STA_IF)
  wlan.active(True)
  
  if(ip != ''):
      wlan.ifconfig((ip,mask,gw,dns))

  wlan.connect(ssid, password)

  while not wlan.isconnected():
      print('Connecting...',ssid)
      time.sleep(1)
  return wlan
  
# ----------------
# Wi-Fiステータス
# ----------------
def wifi_status(wlan):
  ip, subnet, gateway, dns = wlan.ifconfig()
  mac = ":".join(f"{b:02x}" for b in wlan.config('mac'))
  status = {'ip':ip, 'subnet':subnet, 'gateway':gateway, 'dns':dns, 'mac':mac}
  return status

# -------------------------
# LED状態を5秒間隔で通知
# -------------------------
async def task_status(writer):
    writer.write("Pico2w server start\n")
    await writer.drain()
    
    while True:
        state = led.value()
        if state == 1:
            writer.write(b"LED=ON\n")
        else:
            writer.write(b"LED=OFF\n")
        await writer.drain()
        await asyncio.sleep(5)

# -------------------------
# 受信したデータに合わせて処理
# -------------------------
async def task_receiver(reader, writer):
    while True:
        line = await reader.readline()
        try:
            cmd = line.decode("ascii", "ignore").strip().lower()
        except:
            continue
 
        if cmd == "led on":
            led.value(1)

        elif cmd == "led off":
            led.value(0)

        elif cmd in ("help", "-h", "--help"):
            writer.write(
                b"Available commands:\n"
                b"  help, -h  help\n"
                b"  led on    Turn LED ON\n"
                b"  led off   Turn LED OFF\n"
                b"  wifi      Wi-Fi status\n"
                b"  ssid      ssid list\n"
            )

        elif cmd == "wifi":
            status = wifi_status(network.WLAN(network.STA_IF))
            writer.write(b"WiFi STATUS\n")
            writer.write(f"ip  :{status['ip']}\n".encode())
            writer.write(f"mask:{status['subnet']}\n".encode())
            writer.write(f"gw  :{status['gateway']}\n".encode())
            writer.write(f"dns :{status['dns']}\n".encode())
            writer.write(f"mac :{status['mac']}\n".encode())
            
        elif cmd == "ssid":
            ssid_list = wifi_scan()
            for i in ssid_list:
                writer.write(f"ssid:{i['ssid']} rssi:{i['rssi']} \n".encode())
            
        else:
            writer.write(b"ERR: unknown command\n")

        await writer.drain()

# -------------------------
# サーバ処理
# -------------------------
async def handle_client(reader, writer):
    # タスク設定
    await asyncio.gather(task_status(writer),task_receiver(reader, writer))  
    # 終了処理
    await writer.aclose()

# -------------------------
# main()
# -------------------------
async def main():
    # 接続
    ssid='your_SSID'
    password='your_PASSWORD'    
    
    wlan = wifi_connect_with_ip(ssid, password, ip='192.168.0.100')
    print(wifi_status(wlan))

    # サーバー起動
    server = await asyncio.start_server(handle_client, "0.0.0.0", 23)
    while True:
        await asyncio.sleep(1)
        
if __name__ == "__main__":
    asyncio.run(main())

戻る

HTTPアプリ

Telnetに続き、簡易的なHTTPのアプリを作る。サンプルアプリは下記の通り。このソフトをPico 2 W に書き込んで起動すると、ブラウザからhttp://192.168.0.100/でアクセスしてLEDの制御ができる。

関数 処理内容
asyncio.start_server(handle_client, "0.0.0.0", 80) HTTP のポート番号 80 でクライアント接続待ちを行う。クライアントが接続すると handle_client() が呼ばれる。
handle_client(reader, writer) http クライアントが接続すると呼ばれる。
HTML_INDEX クライアントからアクセスされたときに応答するhtmlデータ。

ブラウザでアクセス
image.png

main.py
import network
import time
import asyncio
from machine import Pin

led = Pin("LED", Pin.OUT)

# ----------------
# HTMLデータ
# ----------------
HTML_INDEX = """\
<html>
  <body>
    <h1>Pico2W HTTP Server</h1>
    <p>LED state: {{LED}}</p>
    <p><a href="/on">LED ON</a></p>
    <p><a href="/off">LED OFF</a></p>
  </body>
</html>
"""

# ----------------
# Wi-Fi接続
# ----------------
def wifi_connect_with_ip(ssid, password, ip='', mask='255.255.255.0', gw='192.168.0.1', dns='192.168.0.1'):
  wlan = network.WLAN(network.STA_IF)
  wlan.active(True)
  
  if(ip != ''):
      wlan.ifconfig((ip,mask,gw,dns))

  wlan.connect(ssid, password)

  while not wlan.isconnected():
      print('Connecting...',ssid)
      time.sleep(1)
  return wlan

# ----------------
# Wi-Fiステータス
# ----------------
def wifi_status(wlan):
  ip, subnet, gateway, dns = wlan.ifconfig()
  mac = ":".join(f"{b:02x}" for b in wlan.config('mac'))
  status = {'ip':ip, 'subnet':subnet, 'gateway':gateway, 'dns':dns, 'mac':mac}
  return status

# -------------------------
# HTTPサーバ処理
# -------------------------
async def handle_client(reader, writer):

    # --- 1行目(GET /xxx HTTP/1.1) ---
    request_line = await reader.readline()
    try:
        req = request_line.decode().strip()
    except:
        req = ""

    # --- HTTPヘッダを読み捨て ---
    while True:
        header = await reader.readline()
        if header in (b"\r\n", b"\n", b""):
            break

    # --- LED ON ---
    if req.startswith("GET /on"):
        led.value(1)
        writer.write(b"HTTP/1.1 302 Found\r\nLocation: /\r\n\r\n")

    # --- LED OFF ---
    elif req.startswith("GET /off"):
        led.value(0)
        writer.write(b"HTTP/1.1 302 Found\r\nLocation: /\r\n\r\n")

    # --- HTMLページ表示 ---
    elif req.startswith("GET /"):
        html = HTML_INDEX.replace("{{LED}}", "ON" if led.value() else "OFF")
        response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" + html
        writer.write(response.encode())

    else:
        writer.write(b"HTTP/1.1 404 Not Found\r\n\r\n")

    await writer.drain()
    await writer.aclose()


# -------------------------
# main()
# -------------------------
async def main():
    # 接続
    ssid='your_SSID'
    password='your_PASSWORD'    
    
    wlan = wifi_connect_with_ip(ssid, password, ip='192.168.0.100')
    print(wifi_status(wlan))
    
    # サーバー起動
    server = await asyncio.start_server(handle_client, "0.0.0.0", 80)
 
    while True:
        await asyncio.sleep(1)
        
if __name__ == "__main__":
    asyncio.run(main())

戻る

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?