WiFi 模式 | 說明 |
STA (station 站台) | ESP32 可連線至一路由器 (AP) |
AP (access point 熱點) | 作為熱點讓其他設備連線至 ESP32 |
STA + AP (雙重模式) | 混和模式 (同時開啟 STA 與 AP 功能) |
參考 :
# MicroPython on ESP8266 (五) : WiFi 連線與 WebREPL 測試
# ESP32 官網教學文件 : Networking
# WiFi有兩種mode: STA 與AP (又叫作SoftAP,HostAP)
# Getting Started with MicroPython on ESP32 – Hello World, GPIO, and WiFi
ESP32 的 WiFi 功能寫在內建的 network 模組中, 使用前要先 import 進來, 呼叫 dir() 函數可顯示其內容 :
>>> import network
>>> dir(network)
['__class__', '__init__', '__name__', 'AP_IF', 'AUTH_MAX', 'AUTH_OPEN', 'AUTH_WEP', 'AUTH_WPA2_PSK', 'AUTH_WPA_PSK', 'AUTH_WPA_WPA2_PSK', 'ETH_CLOCK_GPIO0_IN', 'ETH_CLOCK_GPIO16_OUT', 'ETH_CLOCK_GPIO17_OUT', 'LAN', 'MODE_11B', 'MODE_11G', 'MODE_11N', 'PHY_LAN8720', 'PHY_TLK110', 'PPP', 'STAT_ASSOC_FAIL', 'STAT_BEACON_TIMEOUT', 'STAT_CONNECTING', 'STAT_GOT_IP', 'STAT_HANDSHAKE_TIMEOUT', 'STAT_IDLE', 'STAT_NO_AP_FOUND', 'STAT_WRONG_PASSWORD', 'STA_IF', 'WLAN', 'phy_mode']
>>> type(network.AP_IF)
<class 'int'>
>>> type(network.STA_IF)
<class 'int'>
>>> print(network.AP_IF)
1>>> print(network.STA_IF)
0
呼叫 network.WLAN() 建構子並分別傳入 network.AP_IF 與 network.STA_IF 將傳回代表熱點或站台的 WLAN 物件, 呼叫 dir() 可顯示其成員, 共有 8 個方法 :
>>>sta=network.WLAN(network.STA_IF) #傳回 WLAN 物件 (也可傳入 0)
>>>ap=network.WLAN(network.AP_IF) #傳回 WLAN 物件 (也可傳入 1)
>>> type(sta)
<class 'WLAN'>
>>> type(ap)
<class 'WLAN'>
>>> dir(ap) #顯示 WLAN 物件之成員
['__class__', 'active', 'config', 'connect', 'disconnect', 'ifconfig', 'isconnected', 'scan', 'status']
>>> dir(sta) #顯示 WLAN 物件之成員
['__class__', 'active', 'config', 'connect', 'disconnect', 'ifconfig', 'isconnected', 'scan', 'status']
WLAN 物件的 8 個方法說明如下表 :
WLAN 物件的方法 | 說明 |
active([state]) | 設定或查詢無線介面為 up (True) 或 down (False) |
config(param/param=value) | 設定或查詢無線介面之參數如 mac, ssid 等 |
connect(ssid, password) | STA 介面連線外部熱點 |
disconnect() | STA 介面與外部熱點離線 |
ifconfig() | 傳回介面的 IP, netmask,gateway, DNS 位址 (tuple) |
isconnected() | 查詢 STA 介面是否有連線到外部熱點 (True/False) |
scan() | 只用在 STA 物件掃描可供其連線之周圍熱點 |
status([param]) | 查詢 STA 介面指定之參數值 (例如 'rssi') 或目前介面之狀態 (無參數) |
首先測試 active() 方法, 此方法可傳入 True 或 False 來啟動 (up) 或關閉 (down) 無線介面 (setter), 若未傳入參數參數為查詢該介面之狀態 (getter), 同樣是 1.11 版韌體, 與 ESP8266 不同的是, ESP32 的 STA 與 AP 介面預設均為關閉 (down) 狀態 (ESP8266 的 AP 預設是開啟的) :
>>> sta.active()
False #STA 預設為關閉
>>> ap.active()
False #AP 預設為關閉 (ESP32)
在操作 STA 與 AP 介面之前必須即傳入 True 將介面啟動才行, 首先啟動 STA 介面 :
>>> sta.active(True)
I (7552260) phy: phy_version: 4100, 2a5dd04, Jan 23 2019, 21:00:07, 0, 0
I (7552260) wifi: mode : sta (80:7d:3a:b7:a7:5c)
True
>>> I (7552260) wifi: STA_START
可見此時 WiFi 模式為 STA. 接著啟動 AP 介面 :
>>> ap.active(True)
I (7570230) wifi: mode : sta (80:7d:3a:b7:a7:5c) + softAP (80:7d:3a:b7:a7:5d)
I (7570230) wifi: Total power save buffer number: 16
I (7570230) wifi: Init max length of beacon: 752/752
I (7570240) wifi: Init max length of beacon: 752/752
True
>>> I (7570240) network: event 13
先用 scan() 方法掃描周圍的熱點 (注意, 此方法只能用在 STA 介面物件) :
>>> aps=sta.scan()
>>> print(aps)
[(b'TonyNote8', b'\x06\xd6\xaa\x04\xfcK', 11, -66, 3, False), (b'EDIMAX-tony', b'\x80\x1f\x02-Z\x9e', 11, -93, 4, False), (b'EDIMAX', b'\x80\x1f\x02\xef@@', 11, -93, 3, False)]
方法 scan() 會傳回 tuple 組成之串列, 其定義如下 :>>> sta.active()
False #STA 預設為關閉
>>> ap.active()
False #AP 預設為關閉 (ESP32)
在操作 STA 與 AP 介面之前必須即傳入 True 將介面啟動才行, 首先啟動 STA 介面 :
>>> sta.active(True)
I (7552260) phy: phy_version: 4100, 2a5dd04, Jan 23 2019, 21:00:07, 0, 0
I (7552260) wifi: mode : sta (80:7d:3a:b7:a7:5c)
True
>>> I (7552260) wifi: STA_START
可見此時 WiFi 模式為 STA. 接著啟動 AP 介面 :
>>> ap.active(True)
I (7570230) wifi: mode : sta (80:7d:3a:b7:a7:5c) + softAP (80:7d:3a:b7:a7:5d)
I (7570230) wifi: Total power save buffer number: 16
I (7570230) wifi: Init max length of beacon: 752/752
I (7570240) wifi: Init max length of beacon: 752/752
True
>>> I (7570240) network: event 13
可見此時 WiFi 模式變成 STA + AP 雙重模式了, 這時再去查詢介面狀態, 兩個都傳回 True 了 :
>>> sta.active()
True
>>> ap.active()
True
這時呼叫介面的 ifconfig() 方法會傳回一個表示該介面之 IP, netmask, gateway, 與 DNS 位址的 tuple, 例如 :
>>> ap.ifconfig()
('192.168.4.1', '255.255.255.0', '192.168.4.1', '0.0.0.0')
其中 192.168.4.1 就是 ESP32 作為熱點的 IP, 可供其他站台連線. 查詢 STA 介面則全都是 0, 這是因為 STA 介面尚未連線外部熱點之故 :
>>> sta.ifconfig()
('0.0.0.0', '0.0.0.0', '0.0.0.0', '0.0.0.0')
先用 scan() 方法掃描周圍的熱點 (注意, 此方法只能用在 STA 介面物件) :
>>> aps=sta.scan()
>>> print(aps)
[(b'TonyNote8', b'\x06\xd6\xaa\x04\xfcK', 11, -66, 3, False), (b'EDIMAX-tony', b'\x80\x1f\x02-Z\x9e', 11, -93, 4, False), (b'EDIMAX', b'\x80\x1f\x02\xef@@', 11, -93, 3, False)]
(ssid, bssid, channel, RSSI, authmode, hidden)
這裡 bssid 指的是 AP 的 mac 位址, 但上面顯示的 mac 位址似乎怪怪的, 這是因為 mac 為 bytes 類型資料, 用 ASCII 格式顯示時有些會變成亂碼. 解決辦法是用內建的 ubinascii 模組將 binary 轉換成 ASCII 碼, 參考 :
# MAC Address is printed as a bytes object #3115
>>> aps=sta.scan()
>>> aps[0][0]
b'TonyNote8'
>>> type(aps[0][0])
<class 'bytes'>
>>> aps[0][0].decode() #呼叫 decode() 轉成字串
'TonyNote8'
>>> aps[0][1]
b'\x06\xd6\xaa\x04\xfcK'
>>> type(aps[0][1])
<class 'bytes'>
>>> import ubinascii #引入 ubinascii 模組
>>> mac=ubinascii.hexlify(aps[0][1], ':') #轉成以 : 隔開的 16 進位
>>> mac
b'06:d6:aa:04:fc:4a'
>>> type(mac)
<class 'bytes'>
>>> mac.decode() #呼叫 decode() 轉成字串
'06:d6:aa:04:fc:4a'
>>> import ubinascii
>>> for ap in aps:
... ssid=ap[0].decode()
... mac=ubinascii.hexlify(ap[1], ':').decode()
... print('{:>20} {:>20}'.format(ssid, mac))
...
...
...
MicroPython-07a0bc a2:20:a6:07:a0:bc
CHT Wi-Fi(HiNet) 02:16:16:29:e6:94
.1.Free Wi-Fi 12:16:16:29:e6:94
CHT Wi-Fi Auto 00:16:16:29:e6:94
P883 ec:43:f6:f5:ae:a5
17N4F a8:4e:3f:0d:f7:78
上面為了輸出整齊使用了字串的排印格式讓 ssid 與 mac 都有 20 個字元位置 (實際執行很整齊, 但網頁中看不出來), 參考 :
# Python: Format output string, right alignment
完整程式如下 :
import network
import ubinascii
sta=network.WLAN(0)
sta.active(1)
aps=sta.scan()
for ap in aps:
ssid=ap[0].decode()
mac=ubinascii.hexlify(ap[1], ':').decode()
print('{:>20} {:>20}'.format(ssid, mac))
channel 為 WiFi 的頻道或通道, 總共分成 13 個重疊的頻道, 編號 1~13, 其中 1, 6, 11 頻道沒有重疊, 比較不會有干擾問題.
RSSI 是收到的熱點信號強度 (單位 dBm), 是個負值, 越接近 0 信號越強, -50 以內表示信號優良 (Excellent), -50 到 -60 之間信號良好 (Good), -60 到 -70 還可以 (Fair), 低於 -70 就算是弱了 (Weak), -100 則表示完全無信號.
authmode 表示認證加密模式, 其值為整數, 定義如下 :
authmode | 說明 |
0 | open 無加密 |
1 | WEP 加密 |
2 | WPA-PSK 加密 |
3 | WPA2-PSK 加密 |
4 | WPA/WPA2-PSK 加密 |
hidden 表示此熱點是否為隱藏, True 為隱藏, False 為不隱藏 (預設).
若呼叫 connect() 連線外部熱點, 就會獲得熱點派發的網址了 :
>>> sta.connect('TonyNote8', 'blablabla')
>>> I (9620180) wifi: new:<1,1>, old:<1,0>, ap:<1,1>, sta:<1,0>, prof:1
I (9620750) wifi: state: init -> auth (b0)
I (9620770) wifi: state: auth -> assoc (0)
I (9620790) wifi: state: assoc -> run (10)
I (9620840) wifi: connected with TonyNote8, channel 1, bssid = 06:d6:aa:04:fc:4a
I (9620840) wifi: pm start, type: 1
I (9620840) network: CONNECTED
I (9624680) event: sta ip: 192.168.43.177, mask: 255.255.255.0, gw: 192.168.43.1
I (9624680) network: GOT_IP
可見 ESP32 的 STA 介面獲得熱點 (Gateway IP=192.168.43.1) 派發 192.168.43.177 這個 IP. 再次呼叫 ifconfig() 就會傳回 STA 介面的新 IP, netmask, gateway, 與 DNS 位址 :
>>> sta.ifconfig()
('192.168.43.177', '255.255.255.0', '192.168.43.1', '192.168.43.1')
>>> dhcps: send_nak>>udp_sendto result 0
使用連線到同一熱點的筆電 ping ESP32 的 IP 應可獲得回應, 表示網路連線正常 :
C:\Users\User>ping 192.168.43.177
Ping 192.168.43.177 (使用 32 位元組的資料):
回覆自 192.168.43.177: 位元組=32 時間=449ms TTL=255
回覆自 192.168.43.177: 位元組=32 時間=50ms TTL=255
回覆自 192.168.43.177: 位元組=32 時間=257ms TTL=255
回覆自 192.168.43.177: 位元組=32 時間=85ms TTL=255
192.168.43.177 的 Ping 統計資料:
封包: 已傳送 = 4,已收到 = 4, 已遺失 = 0 (0% 遺失),
大約的來回時間 (毫秒):
最小值 = 50ms,最大值 = 449ms,平均 = 210ms
這時呼叫 STA 介面物件的 isconnected() 方法將傳回 True, 這可用來檢測 connect() 是否成功, 以便進入下個執行階段 :
>>> sta.isconnected()
True
由於使用 ESP32 主要是著眼於其網路連線能力, 連線失敗的話後面的作業也不用執行了, 故通常會用一個 while 迴圈來檢查, 例如 :
while not sta.isconnected():
pass
方法 status() 用在查詢 STA 介面之狀態或指定參數如 'rssi' (連線熱點之信號強度), 傳回值為整數, 但查詢 AP 介面時無傳回值或錯誤 (rssi) :
>>> ap.status() #AP 無傳回值
>>> sta.status() #STA 介面傳回狀態值
1010 #已取得 IP
STA 介面的狀態值 1010 表示 'STAT_CONNECTING' (介面連線中), 其他狀態如下表 :
status() 傳回值 | 說明 |
201 | network.STAT_NO_AP_FOUND (搜尋不到 AP) |
202 | network.STAT_WRONG_PASSWORD (密碼錯誤) |
1000 | network.STAT_IDLE (介面閒置中 ) |
1001 | network.CONNECTING (連線中) |
1010 | network.STAT_GOT_IP (已取得 IP) |
>>> sta.status('rssi') #所連線熱點信號強度
-49 #強度 -49dBm
>>> ap.status('rssi') #參數 'rssi' 只定義於 STA介面
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: STA required
方法 config() 用來查詢 (傳入參數字串) 或設定 AP 介面之參數例如 essis, password, channel, 或 mac 等 :
config() 參數 | 說明 |
mac | MAC 位址 (bytes 類型) |
essid | AP 介面的 SSID (str 類型) |
hidden | AP 介面的 SSID 是否隱藏 (boolean 類型) |
channel | AP 介面的 WiFi 通道 1~13 (int 類型) |
authmode | AP 介面的加密認證模式 (int 類型) |
password | AP 介面的登入密碼 (str 類型) |
dhcp_hostname | DHCP 主機名稱 |
這裡的 essid 為 ESS (Extend Service Set 延伸服務群集) 的名稱, ESS 是數個相鄰的 BSS (Basic Service Set 基本服務群集) 組成, 站台 (Station) 在同一個 ESS 範圍內會在各 BSS 間漫遊 (roaming). 簡言之, 一個 AP 就形成一個 BSS, 多個相鄰具有相同 essid 的 AP 就形成一個 ESS, 參考 :
# WiFi 網路的識別: BSS, ESS, SSID, ESSID, BSSID
# 何謂SSID、BSSID、ESSID、HESSID ? @ 香腸炒章魚
# 連線到AP的種類有哪些及天線訊號容易被干擾嗎?
其中 STA 介面可用的僅 mac 與 dhcp_hostname 兩個參數, 其他都是 AP 介面專用. 注意, 查詢參數時, 參數名必須以字串傳入, 例如 ap.config('authmode'), 但設定時不要用字串, 例如 ap.config(authmode=4).
例如 :
>>> ap.config('mac') #查詢 AP 介面之 MAC 位址
b'\x80}:\xb7\xa7]'
>>> ap.config('essid') #查詢 AP 介面之 SSID
'ESP_B7A75D'
>>> ap.config('hidden') #查詢 AP 介面之 SSID 是否隱藏
False
>>> ap.config('channel') #查詢 AP 介面之通道
1
>>> ap.config('authmode') #查詢 AP 介面之加密模式
0
>>> ap.config('password') #查詢 AP 介面之登入密碼 (無此參數)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: unknown config param
>>> ap.config('dhcp_hostname') #查詢 AP 介面之 DHCP 名稱
'espressif'
>>> sta.config('mac') #查詢 STA 介面之 mac 位址
b'\x80}:\xb7\xa7\\'
>>> sta.config('dhcp_hostname') #查詢 STA 介面之 DHCP 名稱
'espressif'
>>> sta.config('authmode') #此參數為 AP 專用
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: AP required
可見 AP 介面的密碼 password 不開放查詢, why?
雖然無法查詢 AP 密碼, 但可以用參數 password 去設定 AP 的密碼. 由於此 1.11 版韌體預設 STA 與 AP 介面都關閉 (v1.10 以前預設開啟 AP), 因此開啟 AP 介面後期預設加密模式為 0 (無加密), 必須先用 authmode 參數更改加密模式, 然後用 password 去設定密碼 (可以同時設定).
>>> ap.config(authmode=4, password='micropythoN')
另外, 上面兩個 mac 位址似乎怪怪的, 怎麼只有三組號碼呢? 這與上面 scan() 傳回的熱點 mac 一樣, 因為 config('mac') 傳回的是 byte 類型的資料, 用 print() 以 ASCII 顯示就變成亂碼了. 解決辦法是使用內建的 ubinascii 模組來轉換 byte 為 ASCII 碼 :
>>> import ubinascii
>>> mac=ubinascii.hexlify(ap.config('mac'), ':')
>>> print(mac)
b'80:7d:3a:b7:a7:5d'
>>> mac=ubinascii.hexlify(sta.config('mac'), ':')
>>> print(mac)
b'80:7d:3a:b7:a7:5c'
此 ubinascii 來自 CPython 的 binascii 模組, 參考 :
我用上傳 binascii.py 到 ESP32 也是可以用的 :
D:\Python\test>ampy --port COM3 put binascii.py /lib/binascii.py
>>> import lib.binascii
>>> mac=lib.binascii.hexlify(ap.config('mac'), ':')
>>> print(mac)
b'80:7d:3a:b7:a7:5d'
>>> ap.config(essid='MYESP32') #設定新的 SSID
I (18657780) wifi: Total power save buffer number: 16
>>> I (18657780) network: event 14
I (18657780) network: event 13
>>> ap.config('essid')
'MYESP32'
但是 channel 似乎固定在通道 1 無法更改 :
>>> ap.config(channel=2) #改為通道 2
I (18739770) wifi: Total power save buffer number: 16
>>> I (18739770) network: event 14
I (18739780) network: event 13
>>> ap.config('channel') #查詢還是通道 1
1
而 password 參數可設定卻無法查詢 :
>>> ap.config(password='123456') #更改 AP 密碼
I (19449800) wifi: Total power save buffer number: 16
>>> I (19449800) network: event 14
I (19449800) network: event 13
>>> ap.config('password') #密碼無法查詢
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: unknown config param
也可以一次設定好幾個參數 :
>>> ap.config(essid='MYESP32', password='123456')
最後呼叫 STA 介面物件之 disconnect() 方法與熱點離線 :
>>> sta.disconnect()
I (20046240) wifi: state: run -> init (0)
I (20046240) wifi: pm stop, total sleep time: 85768549 us / 97553286 us
I (20046240) wifi: new:<11,0>, old:<11,0>, ap:<11,2>, sta:<11,0>, prof:11
>>> I (20046250) wifi: STA_DISCONNECTED, reason:8
>>> I (20067130) event: station ip lost
I (20067130) network: event 8
>>> sta.isconnected()
False
>>> sta.status()
8>>> sta.status('rssi') #沒有連線到熱點
W (20500530) wifi: Haven't to connect to a suitable AP now!
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: Wifi Not Connected
('0.0.0.0', '0.0.0.0', '0.0.0.0', '192.168.43.1')
可見與熱點離線後 IP 又變回 0.0.0.0 了.
最後我將上面的 WiFi 功能整合為如下的 main.py 程式, 系統重啟時會自動執行 main.py 開啟 AP + STA 雙重模式 :
#main.py
import network
import time
import ubinascii
def connectAP(ssid, pwd):
sta.connect(ssid, pwd)
print('Connecting to WiFi AP ...')
time.sleep(8)
if sta.isconnected():
print('Connected: ', sta.ifconfig()[0])
else:
print('Can not connect to AP=' + ssid)
def scanAP():
aps=sta.scan()
for ap in aps:
ssid=ap[0].decode()
mac=ubinascii.hexlify(ap[1], ':').decode()
rssi=str(ap[3]) + 'dBm'
print('{:>20} {:>20} {:>10}'.format(ssid, mac, rssi))
def getIP():
return sta.ifconfig()[0]
ap=network.WLAN(network.AP_IF)
ap.active(True)
ap.config(authmode=4, password='micropythoN')
sta=network.WLAN(network.STA_IF)
sta.active(True)
注意, 由於 ESP 本身熱點 AP 開啟後預設加密模式為 0 (無加密), 此處用 authmode 參數將其設為模式 4 (WPA/WPA2-PSK), 並用 password 設定其密碼為 'micropythoN'. 程式存成 main.py 後利用 ampy 上傳到檔案系統根目錄下 :
D:\ESP32>ampy --port COM8 put main.py
然後開啟 Putty 連線至 ESP32 時會自動重啟, 這時會去執行 main.py, 用 dir() 檢視目前記憶體中的變數, 可見 network, time, ubinascii 等模組, 以及 ap, sta, showAP(), getIP() 等變數都載入記憶體可馬上存取 :
>>> dir()
['uos', 'getIP', 'ap', 'sta', 'bdev', 'time', '__name__', 'showAP', 'ubinascii', 'gc', 'connectAP', 'network']
>>> showAP()
I (62721) network: event 1
TonyNote8 06:d6:aa:04:fc:4a -54dBm
EDIMAX 80:1f:02:ef:40:40 -90dBm
>>> connectAP('TonyNote8','blablabla')
Connecting to WiFi AP ...
I (116151) wifi: ap channel adjust o:1,1 n:6,2
I (116151) wifi: new:<6,0>, old:<1,0>, ap:<6,2>, sta:<6,0>, prof:1
I (116711) wifi: state: init -> auth (b0)
I (116721) wifi: state: auth -> assoc (0)
I (116721) wifi: state: assoc -> run (10)
I (116761) wifi: connected with TonyNote8, channel 6, bssid = 06:d6:aa:04:fc:4a
I (116761) wifi: pm start, type: 1
I (116761) network: CONNECTED
I (119711) event: sta ip: 192.168.43.177, mask: 255.255.255.0, gw: 192.168.43.1
I (119711) network: GOT_IP
Connected: 192.168.43.177
>>> getIP()
'192.168.43.177'
另外一個 main.py 寫法 :
import network
import ubinascii
def connect(ssid, pwd):
if not sta.isconnected():
print('connecting to WiFi AP ...')
sta.connect(ssid, pwd)
while not sta.isconnected():
pass
print('Connected: ', sta.ifconfig()[0])
def scan():
aps=sta.scan()
for ap in aps:
ssid=ap[0].decode()
mac=ubinascii.hexlify(ap[1], ':').decode()
rssi=str(ap[3]) + 'dBm'
print('{:>20} {:>20} {:>10}'.format(ssid, mac, rssi))
def ip():
return sta.ifconfig()[0]
ap=network.WLAN(network.AP_IF)
ap.active(True)
ap.config(authmode=4, password='micropythoN')
sta=network.WLAN(network.STA_IF)
sta.active(True)
參考 :
# MicroPython 功能集
# ESP32 MicroPython: Connecting to a WiFi Network
# https://docs.micropython.org/en/latest/library/network.WLAN.html
# https://docs.micropython.org/en/latest/wipy/tutorial/wlan.html
# BugWorkShop - 甲蟲工作室
# MicroPython on ESP32
# MicroPython Programming with ESP32 and ESP8266 eBook
# 如何選擇最佳的 Wi-Fi 無線網路頻道,獲得最佳的傳輸速度
# 連線到AP的種類有哪些及天線訊號容易被干擾嗎?
# 什么是RSSI以及它与Wi-Fi网络的关系
2019-07-05 補充 :
我用 ESP-01 模組燒錄 v1.11 版韌體測試, 發現同樣版本的韌體, WALN 介面的預設是不同的 :
- ESP8266 : STA 關閉, AP 已開啟, 加密模式 4, 預設密碼 micropythoN
- ESP32 : STA 關閉, AP 關閉
2019-07-09 補充 :
我發現上面那個 connectAP() 的寫法不妥, 當 ssid 或 password 錯誤時會因為連線不成功而在 while 迴圈裡無限循環, 應該改成如下, 使用 time 模組預設 8 秒來等候連線 :
#main.py
import network
import ubinascii
import time
def connect(ssid, pwd):
sta.connect(ssid, pwd)
print('Connecting to WiFi AP=', ssid, ' ...')
time.sleep(8)
if sta.isconnected():
print('Connected: ', sta.ifconfig()[0])
else:
print('Can not connect to AP=' + ssid)
def scan():
aps=sta.scan()
for ap in aps:
ssid=ap[0].decode()
mac=ubinascii.hexlify(ap[1], ':').decode()
rssi=str(ap[3]) + 'dBm'
print('{:>20} {:>20} {:>10}'.format(ssid, mac, rssi))
def ip():
return sta.ifconfig()[0]
ap=network.WLAN(network.AP_IF)
ap.active(True)
ap.config(authmode=4, password='micropythoN')
sta=network.WLAN(network.STA_IF)
sta.active(True)
或者寫在 boot.py 裡面亦可, 把 main.py 留給應用程式, 但要保留 boot.py 裡面原來的程式設定, 例如 gc 與 webrepl 等.
2019-07-15 補充 :
今天在 NodeMCU-32S 板子上燒錄 bpi:bit 的韌體 (v1.10), 結果是可用的 (還沒發現是否有綁硬體的地方, 例如板上內建之感測器或致動器), 燒錄部分參考下面這篇的結尾 :
# bpi:bit ESP32 開發板測試 (一) : 燒錄 MicroPython 韌體
bpi::bit 最新 MicroPython 韌體下載網頁 :
# https://github.com/BPI-STEAM/BPI-BIT-MicroPython/releases
我燒錄的是 firmware-20190623.bin 映像檔, 完成後用 ampy --port com3 get boot.py 檢查開機檔, 發現開機時會先載入自訂的 wifi 模組, 然後呼叫 wifi.ready() 方法 :
import esp
esp.osdebug(None)
#import webrepl
#webrepl.start()
import wifi
wifi.ready()
用 dir() 檢查記憶體中的系統預載物件, 發現確實有一個 wifi 物件存在, 以 type() 檢視發現它是一個模組 :
>>> dir()
['bdev', 'bluetooth', '__name__', 'lines', 'uos', 'esp', 'wifi', 'gc', 'line']
>>> type(wifi)
<class 'module'>
呼叫 wifi.ready() 發現它的作用其實是用來在開機時選擇 smartconfig 用的 :
Press "A" to enter the smartconfig mode while the led is rolling
Started wifi in normal mode
用 dir(wifi) 檢視 wifi 模組, 發現它的架構有點怪, 裡面還包含了 network 模組, 與 MicroPython 原本的 network 模組比對發現, 其實 network 與 wifi.network 這兩個是一樣的 :
>>> dir(wifi)
['__class__', '__name__', 'close', 'disconnect', 'ifconfig', 'isconnected', 'network', 'smartconfig', 'wlan', 'try_connect', 'is_smartconfig', '__irq_sc', 'ready']
>>> type(wifi.network) #指向 network 模組
<class 'module'>
>>> dir(wifi.network)
['__class__', '__init__', '__name__', 'AP_IF', 'AUTH_MAX', 'AUTH_OPEN', 'AUTH_WEP', 'AUTH_WPA2_PSK', 'AUTH_WPA_PSK', 'AUTH_WPA_WPA2_PSK', 'ETH_CLOCK_GPIO0_IN', 'ETH_CLOCK_GPIO16_OUT', 'ETH_CLOCK_GPIO17_OUT', 'LAN', 'MODE_11B', 'MODE_11G', 'MODE_11N', 'PHY_LAN8720', 'PHY_TLK110', 'PPP', 'STAT_ASSOC_FAIL', 'STAT_BEACON_TIMEOUT', 'STAT_CONNECTING', 'STAT_GOT_IP', 'STAT_HANDSHAKE_TIMEOUT', 'STAT_IDLE', 'STAT_NO_AP_FOUND', 'STAT_WRONG_PASSWORD', 'STA_IF', 'WLAN', 'mDNS', 'phy_mode', 'smartconfig']
>>> import network
>>> dir(network)
['__class__', '__init__', '__name__', 'AP_IF', 'AUTH_MAX', 'AUTH_OPEN', 'AUTH_WEP', 'AUTH_WPA2_PSK', 'AUTH_WPA_PSK', 'AUTH_WPA_WPA2_PSK', 'ETH_CLOCK_GPIO0_IN', 'ETH_CLOCK_GPIO16_OUT', 'ETH_CLOCK_GPIO17_OUT', 'LAN', 'MODE_11B', 'MODE_11G', 'MODE_11N', 'PHY_LAN8720', 'PHY_TLK110', 'PPP', 'STAT_ASSOC_FAIL', 'STAT_BEACON_TIMEOUT', 'STAT_CONNECTING', 'STAT_GOT_IP', 'STAT_HANDSHAKE_TIMEOUT', 'STAT_IDLE', 'STAT_NO_AP_FOUND', 'STAT_WRONG_PASSWORD', 'STA_IF', 'WLAN', 'mDNS', 'phy_mode', 'smartconfig']
再來看一下 wifi.wlan, 其實這是一個呼叫 network.WLAN(network.STA_IF) 後建立的 WLAN 物件 (STA), 我們可以自己建立一個 WLAN 物件來比較 (預設是 STA) :
>>> type(wifi.wlan)
<class 'WLAN'>
>>> dir(wifi.wlan)
['__class__', 'active', 'config', 'connect', 'disconnect', 'ficonfig', 'isconnected', 'scan', 'status']
>>> w=network.WLAN()
>>> dir(w)
['__class__', 'active', 'config', 'connect', 'disconnect', 'ifconfig', 'isconnected', 'scan', 'status']
可見 wifi.wlan 是一個 STA 介面的 WLAN 物件, 可以呼叫 scan() 來掃描周圍 AP, 然後呼叫 connect() 與 AP 連線, 所有 STA 的方法都能呼叫 :
>>> wifi.wlan.scan()
[(b'TonyNote8', b'\x06\xd6\xaa\x04\xfcK', 11, -60, 3, False), (b'EDIMAX-meinung', b'\x80\x1f\x02G\x1e\xa8', 11, -75, 4, False)]
>>> type(network.WLAN)
<class 'function'>
>>> wifi.wlan.connect('TonyNote8','blablabla')
>>> wifi.wlan.isconnected() True
>>> wifi.wlan.status()
1010
>>> wifi.wlan.ifconfig()
('192.168.43.68', '255.255.255.0', '192.168.43.1', '192.168.43.1')
>>> wifi.wlan.status('rssi')
-38
>>> wifi.wlan.config('mac')
b'0\xae\xa4\x8f\xc6L'
>>> wifi.wlan.config('essid')
'TonyNote8'
>>> wifi.wlan.config('dhcp_hostname')
'espressif'
總之, 若使用 bpi::bit 的 MicroPython 韌體, 在開機後就已經有 wifi.wlan 這個 STA 介面物件可直接使用, 而且要用到 network 模組時也是直接用 wifi.network 即可, 不須再 import network.
不過, 若要用到 AP 介面 (例如架設網頁伺服器) 則須自行建立, 不過方便的地方是如上所述, 可直接用 wifi.network.WLAN(1) 來建立 AP 介面物件, 不必再 import network :
>>> ap=wifi.network.WLAN(1)
>>> type(ap)
<class 'WLAN'>
>>> dir(ap) #與 STA 一樣, 但 scan() 與 connect() 等無作用
['__class__', 'active', 'config', 'connect', 'disconnect', 'ifconfig', 'isconnected', 'scan', 'status']
>>> ap.active()
False
>>> ap.active(True)
True
>>> ap.ifconfig()
('192.168.4.1', '255.255.255.0', '192.168.4.1', '0.0.0.0')
>>> ap.config('mac')
b'0\xae\xa4\x8f\xc6M'
>>> ap.config('essid')
'ESP_8FC64D'
>>> ap.config('hidden')
False
>>> ap.config('channel')
11
>>> ap.config('authmode')
0
>>> ap.config('dhcp_hostname')
'espressif'
可見 AP 預設的認證模式是 0 (未加密), 最好用 config() 將其改為 4 :
>>> ap.config(authmode=4, password='micropythoN')
>>> ap.config('authmode')
4
>>> ap.config('password')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: unknown config param
此處為了測試方便, 我將其設定為舊版韌體 AP 的預設密碼 'micropythoN', 實際上線運轉時務必更改. 同樣地, 密碼是不能查詢的. 整體來說, 我覺得 bpi::bit 的韌體比官方的更好用.
您好!請問為何當main.py put到ESP32後就會無法再與電腦連線呢?不論是用ampy或用uPyCraft都沒辦法連線,只能重新erase_flash!
回覆刪除Hi, ampy 會跟 webrepl 沖突 (搶 com port), 所以用 ampy 時 webrepl 要關掉網頁. 參考 :
回覆刪除http://yhhuang1966.blogspot.com/2017/05/micropython-on-esp8266_18.html
作者已經移除這則留言。
回覆刪除請問network模組要去哪下載,我用pip3 install network的方式下載的沒有WLAN這個類別
回覆刪除Hi, network 模組是 MicroPython 內建, 燒錄韌體後即已有, 不需安裝喔.
回覆刪除