🐝

ESP32をWiFiに接続する

2023/04/06に公開


はじめに

この記事ではESP32開発ボードをMicropythonを使用してWiFiに接続する方法について記述しています。

In English

This article describes how to connect ESP32 development board to WiFi network using Micropython in Japanese.

ESP32開発ボードとWiFi

原稿執筆時の段階では、ESP32開発ボードで使用できるWiFiは2.4GHz帯のみで、5GHz帯には対応していないので、注意が必要です。

ESP32のWiFiインターフェイスにはステーションモードアクセスポイントモード のふたつのモードがあります。

ステーションモードは自身(ESP32)が無線LAN子機としてWiFiルータなどのアクセスポイントに接続するモードです。

アクセスポイントモードは自身がアクセスポイントとして振る舞い、PCやスマホなどの無線LAN子機からの接続を受け付けるモードです。

結線

結線は不要です。

サンプルプログラム1(WiFiスキャン)

次のプログラムはESP32のWiFiインターフェイスをステーションモードに設定し、1秒毎にWiFiをスキャン(周辺で、どのようなSSIDの電波が出ているか調べる)を5回行うプログラムです。

7行目:ESP32のWiFiインターフェイスをステーションモードとして使用するようインスタンスを作成しています。
10行目:WiFiインターフェイスを有効化しています。
14行目:scan()を実行し、その結果を表示しています。

altwlan_scan1.py
wlan_scan1.py

コピペ用 wlan_scan1.py
wlan_scan1.py
# Scan WiFi network aroud ESP32

import network
import time

# Make wlan instance as station
wlan = network.WLAN(network.STA_IF)

# Activate WiFi interface
wlan.active(True)

# Scan 5 times every 1 second
for n in range(5):
    print(wlan.scan())
    time.sleep(1)
    

wlan.scan()の戻り値は次のようなタプルになっています。

(ssid, bssid, channel, RSSI, security, hidden)

ssid
Service Set Identifierの略で、無線LANアクセスポイントの識別名。
最大32文字。

bssid
Basic Service Set Identifierの略で、通常はMACアドレスとなっている。

channel
使用しているWiFiのチャンネル番号を表す。

RSSI
Received Signal Strength Indicatorの略で、受信しているWiFi電波の強度を表す。単位はdBm(でぃーびーえむ)。0に近づくほど電波が強い。

security
WiFiの認証方式を表し、open, WEP, WPA-PSK, WPA2-PSK, WPA/WPA2-PSKの値がそれぞれ、0から4で返される。

hidden
0はSSIDを表示し、1はSSIDを表示しない(SSIDステルス機能)モードとなっている。

サンプルプログラム2(スキャン結果の詳細表示)

wlan.scan()の戻り値を詳しく表示するようにサンプルプログラム1を変更したものです。

17行目:scandataというリストに格納されたデータを22行目以下で、一つずつ取り出して表示しています。
23行目:SSIDがbyte型となっているので、decode()でstring型に変換して表示います。
24行目:MACアドレスがbyte型で格納されているので、:(コロン)を区切り記号とした16進数表現に変換(binascii.hexlify())し、さらにbyte型をstring型に変換(decode())して表示しています。
25行目、26行目:チャネルやRSSIをstring型に変換して表示しています。
27行目:WiFiの認証方式を8行目のSECURITYリストから取り出して表示しています。

altwlan_scan2.py
wlan_scan2.py

コピペ用 wlan_scan2.py
wlan_scan1.py
# Scan WiFi network aroud ESP32-Dev board
# Dec. 30 2022 iot101@zenn.dev

import network
import binascii  # for MAC address

# WiFi Authentication Method
SECURITY = ["open", "WEP", "WPA-PSK", "WPA-PSK", "WPA/WPA2-PSK"]

# Make wlan instance as station
wlan = network.WLAN(network.STA_IF)

# Activate WiFi interface
wlan.active(True)

# Scan once
scandata = wlan.scan()
print(scandata)
print()

# Print details
for data in scandata:
    print("SSID:", data[0].decode())
    print("MACADR:", binascii.hexlify(data[1], ':').decode())
    print("Channel:", str(data[2]))
    print("RSSI:", str(data[3]))
    print("Security:", SECURITY[data[4]])
    print()


サンプルプログラム3(WiFiルータ接続)

ESP32開発ボードをステーションモードでWiFiルータに接続するサンプルプログラムです。
10行目、11行目のSSIDPASSは使用する環境に合わせて設定する必要があります。
接続が完了すると、DHCPで得られたIPアドレス、ゲートウェイ、DNSサーバ、RSSIが表示されます。
接続できない場合は、延々と . (ドット)が表示され続けるので、CTRL-Cでプログラムを停止させて下さい。

17行目〜20行目:一旦WiFiインターフェイスを無効化した後に有効化しています。
24行目:WiFiが接続されていなかったら10行目と11行目でセットしたSSIDとPASSを用いて、26行目でWiFiに接続します。
28行目:接続されるまで「.」を表示して200ミリ秒待つことを繰り返します。
34行目:接続されたWiFiルータの電波強度(電界強度)を取得しています。
35行目:接続されたWiFiのIPアドレス、ネットマスク、デフォルトルート、ネームサーバの情報を取得しています。

altwifi1.py

コピペ用 wifi1.py
wifi1.py
# Try to connect WiFi router
# Dec. 4th 2022
# (C) iot101@zenn.dev

import network
import time

# At home, you should change these 2 lines.
SSID = "SSID_at_home_nomap"
PASS = "PASS_at_home"

# Connect to Wifi
print()
print("WiFi initializing...")
wlan = network.WLAN(network.STA_IF)
wlan.active(False)
time.sleep(1)
wlan.active(True)
time.sleep(1)
print("Scanning WLAN...")
print(wlan.scan())

if not wlan.isconnected():
    print("Connecting WiFi:",SSID, end="")
    wlan.connect(SSID, PASS)
    time.sleep_ms(100)
    while not wlan.isconnected():
        print(".", end='')
        time.sleep_ms(200)  
        pass

print("\n\nConnected.")
rssi = wlan.status('rssi')
myip, mynetmask, myroute, mydns = wlan.ifconfig()
wifiinfo = (myip, myroute, mydns, rssi)
print("ip: %s\ngateway: %s\ndns: %s\nrssi: %.1f" % wifiinfo)

実行結果

WiFi initializing...
Scanning WLAN...
[(b'myoffice_nomap', b'xxx\xxx\xxx\xxx', 6, -47, 3, False), (b'Buffalo-G-Z123', b'\xxxx\xxx\xxx\xxx', 9, -76, 3, False), (b'MicroPython-xxxxxx', b'J?\xxxx\xxx', 1, -78, 4, False)]
Connecting WiFi: myoffice_nomap....

Connected.
ip: 192.168.134.123
gateway: 192.168.134.233
dns: 192.168.134.233
rssi: -47.0

サンプルプログラム4(複数拠点でのルータ接続)

自宅のWiFiと職場/学校のWiFiなど、複数箇所に移動してWiFi接続する場合のサンプルコードです。
mywifiというリストにそれぞれの場所のSSIDとパスワードをタプルでセットするようにしています。
24行目のMAXTRY変数で設定された回数接続を試みて、接続できなければ諦めて次へ行きます。
WiFiに接続すると、ESP32開発ボードのオンボードLEDが点灯したままになり、接続できないと消灯します。
SSID1,PASS1,SSID2,PASS2はそれぞれの環境に合わせて書き換えて下さい。

このファイルをboot.pyというファイル名に変更し、ESP32開発ボードに転送すると、起動時にWiFi接続するようにできます。

14、15行目、18、19行目:それぞれで場所のSSIDとPASSを設定します。環境に合わせて書き換える必要があります。
21行目:SSIDとPASSの組み合わせをタプルとして、それらからリストを作成しています。
24行目:試行する接続回数の上限をセットしています。
34行目〜40行目:オンボードLEDを0.2秒ごとに点滅する関数です。
44行目〜87行目:WiFiに接続する関数です。WiFiが接続されていなかったら(55行目)SSIDとPASSの組みを順に取り出して接続を試みます。MAXTRYの値を超えるとGive upします。接続されるとDHCPで割り振られたIPアドレス、デフォルトゲート、DNSサーバ、電波強度、MACアドレスを表示します。

altwifi2.py

コピペ用 wifi2.py
wifi2.py
# Try to connect WiFi at multiple place
# Dec. 4th 2022
# Last modify: Apr. 13 2023
# (C) iot101@zenn.dev
#
# Rename this file to boot.py and put this into ESP board.

import network
import machine
import time
import binascii  # for MAC address

# At home, you should change these 2 lines.
SSID1 = "SSID_at_home_nomap"
PASS1 = "PASS_at_home"

# At office/school, you should change these 2 lines.
SSID2 = "SSID_at_office/school"
PASS2 = "PASS_at_office/school"

mywifi = [(SSID1, PASS1), (SSID2, PASS2)]

# try to connect wifi MAXTRY times
MAXTRY = 10

wlan = ""

# on-board LED
LED = 2
led = machine.Pin(LED, machine.Pin.OUT)


# blink on-board led count times
def blink(count):
    for n in range(count):
        led.on()
        time.sleep(0.2)
        led.off()
        time.sleep(0.2)
    led.off()


# Connect to Wifi
def connectwifi():
    print()
    print("WiFi initializing...")
    wlan = network.WLAN(network.STA_IF)
    wlan.active(False)
    time.sleep(1)
    wlan.active(True)
    time.sleep(1)
    print("Scanning WLAN...")
    print(wlan.scan())

    for w in mywifi:
        if not wlan.isconnected():
            trycount = 0
            ssid, passwd = w
            print("Trying to connect WiFi: "+ssid, end='')
            wlan.connect(ssid, passwd)
            time.sleep_ms(100)
            while not wlan.isconnected():
                blink(1)
                print(".", end='')
                time.sleep_ms(200)
                trycount += 1

                # Give up if trycount exceeded
                if trycount > MAXTRY:
                    wlan.disconnect()
                    print("Give up")
                    wifiinfo = (None, None, None, None, None)
                    break


    if wlan.isconnected() is True:
        rssi = wlan.status('rssi')
        macaddr = binascii.hexlify(wlan.config('mac'), ':').decode()
        print("\n\nConnected WiFi:", ssid)
        myip, mynetmask, myroute, mydns = wlan.ifconfig()
        wifiinfo = (myip, myroute, mydns, rssi, macaddr)
        print("ip: %s\ngateway: %s\ndns: %s\nrssi: %.1f\nmacaddr: %s"
              % wifiinfo)
        led.on()

    return wifiinfo


# ---
blink(5)
myip, myroute, mydns, rssi, macaddr = connectwifi()
if myip is None:
    print("Can't connect WiFi.")

演習問題

  1. ESP32開発ボードと同一のWiFiに接続されているPCからpingを飛ばして疎通確認を行って下さい。スマフォに「Network Analyzer」など、pingができるアプリがインストールされていれば、スマートフォンからも試せます。ただし、ESP32開発ボードと同じWiFiに接続されている必要があります。

スマホ用Network Analyzerについては以下の画像のリンクをクリックして下さい。
 アプリのインストールは自己責任でお願いします。
altAppStore altGooglePlay

  1. ESP32開発ボードとPCをUSBケーブルで接続されている場合はThonnyのシェル画面でESP32開発ボードのIPアドレスを知ることができますが、モバイルバッテリなどから電源が供給されていて、PCが接続されていない場合、どうすればESP32開発ボードのIPアドレスを知ることができますか?

ヒント:次のようなキーワドで ChatGPTに聞いてみて下さい。
「ローカルネットに接続された機器のIPアドレスを知る方法は? ただし、MACアドレスは既知である。」

MACアドレスはサンプルプログラム4でもシェル画面の最後に表示されますが、下のプログラムを単独に動かしても表示させることができます。

wlan_mymacaddress.py
# Display MAC address
# Apr. 7th 2023
# (C) iot101@zenn.dev

import network
import binascii

wlan = network.WLAN(network.STA_IF)
wlan.active()

macaddr = wlan.config('mac')
print("My MAC address is", binascii.hexlify(macaddr, ":").decode())

おまけ

ステーションモードで固定IPにするサンプルプログラム

サンプルプログラム3、4はDHCPによりWiFiルータからIPアドレスなどが付与されますが、固定IPとして設定することもできます。

14行目〜17行目:固定IPに必要な情報をセットします。
43行目:IPアドレス等の情報を保持したタプルを作成しています。
44行目:43行目の情報にしたがってWiFiインターフェースの設定を行っています。

altwifi_fixedIP.py

コピペ用 wifi_fixedIP.py
wifi_fixedIP.py
# Try to connect WiFi router
# Apr. 6th 2023
# (C) iot101@zenn.dev

import network
import machine
import time

# At home, you should change these 2 lines.
SSID = "SSID_at_home_nomap"
PASS = "PASS_at_home"

# Fixed IP information
MYIP = '192.168.0.4'
MYNETMASK = '255.255.255.0'
MYGATEWAY = '192.168.0.1'
MYDNS = '1.1.1.1'

# Connect to Wifi
print()
print("WiFi initializing...")
wlan = network.WLAN(network.STA_IF)
wlan.active(False)
time.sleep(1)
wlan.active(True)
time.sleep(1)
print("Scanning WLAN...")
print(wlan.scan())

if not wlan.isconnected():
    print("Connecting WiFi:",SSID, end="")
    wlan.connect(SSID, PASS)
    time.sleep_ms(100)
    while not wlan.isconnected():
        print(".", end='')
        time.sleep_ms(200)  
        pass

print("\n\nConnected.")
rssi = wlan.status('rssi')

# set fixed IP
MYWIFI = (MYIP, MYNETMASK, MYGATEWAY, MYDNS)
wlan.ifconfig(MYWIFI)

myip, mynetmask, myroute, mydns = wlan.ifconfig()
wifiinfo = (myip, myroute, mydns, rssi)
print("ip: %s\ngateway: %s\ndns: %s\nrssi: %.1f" % wifiinfo)

アクセスポイントモードのサンプルプログラム

ESP32開発ボードをWiFiのアクセスポイントとして使用するサンプルプログラムです。
パスワードは8桁以上にする必要があります。さもないと、Wifi Invalid Passwordのエラーが出ます。

このプログラムを動かした後、スマホやPCのWiFiをESP32開発ボードのSSID(ESP32_AccessPoint)に接続するとそれらのMACアドレスが表示されます。パスワードはHimitsudesuと入力して下さい。
接続を切るとMACアドレスが表示されなくなります。DHCPで割り振られたIPアドレスはPCやスマホで確認できます。

altwifi_accesspoint.py

コピペ用 wifi_accesspoint.py
wifi_accesspoint.py
# Become an access point
# Apr. 6th 2023
# (C) iot101@zenn.dev

import network
import binascii
import time

# Access point credensials
SSID = 'ESP32_AccessPoint'
# THe password must be more than 8 digits or
# you will meet 'Wifi Invalid Password' error.
PASS = 'Himitsudesu'
# Authentification mode. We use WPA/WPA2-Personal.
AUTH = network.AUTH_WPA_WPA2_PSK

# Access Point address information
MYIP = '172.16.3.254'
MYNETMASK = '255.255.255.0'
MYGATEWAY = '172.16.3.254'
MYDNS = '172.16.3.254'
MYNETINFO = (MYIP, MYNETMASK, MYGATEWAY, MYDNS)

# Connect to Wifi
print()
print("WiFi initializing...")
wlan = network.WLAN(network.AP_IF)
wlan.config(essid=SSID, password=PASS, authmode=AUTH)

wlan.active(False)
time.sleep(1)
wlan.active(True)

# Wait until WiFi interface is active
while wlan.active() is False:
    pass

print('Access point has bee activated')

wlan.ifconfig(MYNETINFO)
print(wlan.ifconfig())

# Check who is connected via WiFi, show MAC address
while True:
    clients = wlan.status('stations')

    if len(clients) == 0:
        print("Waiting for connection...")
    else:
        for client in clients:
            mac = client
            print(binascii.hexlify(mac[0], ":").decode(), " ", end='')
        print()
    time.sleep(1)

Discussion