之前有問過賣家, 這兩塊板子都是 4MB Flash, WeMOS NodeMCU v2 這塊出廠搭載 NodeMCU, 而 D1 Mini 則無, 所以焊好後就來給 D1 Mini 灌 MicroPython 測試看看. WeMOS D1 Mini 的微控器是 ESP-12EX 模組, 運作於 3.3V 電壓位準, 拉出 9 個 GPIO 輸出入腳, 2 個 UART 腳, 以及一個 ADC(A0) 類比輸入腳, 板子左下方還有一個 Reset 按鈕, 規格詳見官網 :
# https://wiki.wemos.cc/products:d1:d1_mini
教學文件參考 :
# http://micropython-on-wemos-d1-mini.readthedocs.io/en/latest/index.html
ADC(A0) 的類比輸入準位為 0~3.3V, 經 ESP8266 內部 ADC 轉換為 0~1023 的整數, 高於 3.3V 的類比信號須經電阻分壓電路降壓至最大信號時為 3.3V. 另外, 在 MicroPython 中控制 GPIO 腳須以 ESP8266 腳位編號為對象, 不可用 D1 Mini 板子上的 D0~D8, 其對照如下 :
板子腳位 | ESP8266 腳位 | 功能 |
D0 | GPIO16 | IO |
D1 | GPIO5 | IO, SCL |
D2 | GPIO4 | IO, SDA |
D3 | GPIO0 | IO, 內建 10K 上拉電阻 |
D4 | GPIO2 | IO, 內建 10K 上拉電阻與 LED |
D5 | GPIO14 | IO, SCK |
D6 | GPIO12 | IO, MISO |
D7 | GPIO13 | IO, MOSI |
D8 | GPIO15 | IO, SS, 內建 10K 上拉電阻 |
TX | TXD | UART 送端 |
RX | RXD | UART 收端 |
A0 | ADC | 類比輸入 (0~3.3V) |
注意, 在燒錄 MicroPython 韌體前必須將 D3 (GPIO0) 接地再插入 Micro USB 開機, 這樣才會進入 Flash 燒錄模式. 然後開啟 ESP8266Flasher 軟體, 選定所接的 COM 埠, 指定要燒錄之韌體位置與 4MB 的 Flash 大小, 最新韌體可在下列網址下載 :
# https://micropython.org/download/esp8266/
然後按 Flash 鈕即會看到 ESP8266 的 MAC 位址顯示並開始燒錄, 過程中 D1 Mini 板上的藍色 LED 燈會閃爍, 表示韌體正寫入 Flash 中, 完成後左下角的 NODEMCU TEAM 前面會打勾 :
注意, ESP-12 系列的 Flash 記憶體都是 4MB.
韌體燒錄說明參見 :
# MicroPython on ESP8266 (一) : 燒錄韌體
# MicroPython v1.9.1 版韌體測試
燒錄完成後拔除 D3 (GPIO0) 的接地線重開機 (按 Reset 鈕或插拔 USB), 開啟 PuTTY 用 Serial 速率 115200 連線 D1 Mini, 按 Ctrl+B 即進入 REPL 介面, 用 os.listdir() 可知檔案目錄下面只有一個 boot.py 檔案 :
MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
>>> import os
>>> os.listdir()
['boot.py']
>>> f=open('boot.py','r')
>>> lines=f.readlines()
>>> for line in lines:
... print(line)
...
...
...
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
import gc
#import webrepl
#webrepl.start()
gc.collect()
>>>
然後開啟 Web REPL 功能 :
>>> import webrepl_setup
WebREPL daemon auto-start status: disabled
Would you like to (E)nable or (D)isable it running on boot?
(Empty line to quit)
> E (開啟 webrepl 功能)
To enable WebREPL, you must set password for it
New password: 123456
Confirm password: 123456
Changes will be activated after reboot
Would you like to reboot now? (y/n) Y
Would you like to reboot now? (y/n) y (要用小寫 y)
寫入完成後會要求重啟 :
l▒▒|▒▒#4 ets_task(40100164, 3, 3fff829c, 4)
WebREPL daemon started on ws://192.168.4.1:8266
Started webrepl in normal mode
OSError: [Errno 2] ENOENT
MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
更多檔案操作參考 :
# MicroPython on ESP8266 (六) : 檔案系統測試
接下來參考之前伺服器測試這篇 :
# MicroPython on ESP8266 (十四) : 網頁伺服器測試
用記事本編輯如下 main.py 檔案 :
#main.py
import time
WAIT_FOR_CONNECT=8
def set_ap():
html="""
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
%s
</body>
</html>
"""
form="""
<form method=get action='/update_ap'>
<table border="0">
<tr>
<td>SSID</td>
<td><input name=ssid type=text></td>
</tr>
<tr>
<td>PWD </td>
<td><input name=pwd type=text></td>
</tr>
<tr>
<td></td>
<td align=right><input type=submit value=Connect></td>
</tr>
</table>
</form>
"""
import socket
addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
s=socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
while True:
cs, addr=s.accept()
print('client connected from', addr)
data=cs.recv(1024)
request=str(data,'utf8')
print(request, end='\n')
if request.find('update_ap?') == 5:
para=request[request.find('ssid='):request.find(' HTTP/')]
ssid=para.split('&')[0].split('=')[1]
pwd=para.split('&')[1].split('=')[1]
sta.connect(ssid,pwd)
while not sta.isconnected():
pass
print('Connected:IP=',sta.ifconfig()[0])
cs.send(html % 'Connected:IP=' + sta.ifconfig()[0])
else:
cs.send(html % form)
cs.close()
s.close()
import network
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
set_ap()
else:
print('Connected:IP=', sta.ifconfig()[0])
#Application code is written here or import from a separate file
#import myapp
#myapp.main()
存成 main.py 後用 ampy 將其上傳到 D1 Mini 裡 :
D:\test>ampy --port COM4 put main.py
然後按 Ctrl+D 軟開機 :
PYB: soft reboot
#9 ets_task(40100164, 3, 3fff829c, 4)
WebREPL is not configured, run 'import webrepl_setup'
Connecting to AP ... (連線到最近一次設定之無線基地台, 等候 8 秒)
Connected:IP= 192.168.2.111 (連線成功)
MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
>>>
如果顯示 "listening on 192.168.4.1", 表示上次設定的 AP 目前收不到訊號無法連線, 這時打開手機 WiFi 功能, 找尋 D1 Mini 的 ESP8266 內建的 AP, 其 SSID 是以 "MicroPython-" 開頭 (後面是 MAC 後 6 碼), 以內建密碼 "micropythoN" 連線此 AP :
SSID=MicroPython-c111d1
PWD=micropythoN
再打開手機瀏覽器連線 192.168.4.1, 會看到一個 AP 設定網頁, 輸入欲連線之 AP 的 SSID 與 PWD 後按 Connect 即可, 這時 D1 Mini 重開機即可連線至該無線基地台了.
接著修改上面 main.py 的最後兩行, 拿掉前面的 #, 變成 :
#myapp.py
import myapp
myapp.main()
重新上傳 main.py, 然後參考下面這篇的測試 4 : "建立網頁伺服器顯示 GPIO 輸入腳的狀態" :
# MicroPython on ESP8266 (十四) : 網頁伺服器測試
但要修改第二行的 pins 串列內的元組, 因為那篇是針對 ESP-01 模組, 它只有 GPIO0 與 GPIO2 兩支輸出入腳, 而 D1 Mini 卻有 9 支 GPIO 腳, 然後存成 myapp.py :
#myapp.py
import machine
pins=[machine.Pin(i, machine.Pin.IN) for i in (0, 2, 4, 5, 12, 13, 14, 15, 16)]
html="""<!DOCTYPE html>
<html>
<head> <title>ESP8266 Pins</title> </head>
<body> <h1>ESP8266 Pins</h1>
<table border="1"> <tr><th>Pin</th><th>Value</th></tr> %s </table>
</body>
</html>
"""
import socket
addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
s=socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
while True:
cs, addr=s.accept()
print('client connected from', addr)
data=cs.recv(1024)
print(str(data,'utf8'), end='\n')
rows=['<tr><td>%s</td><td>%d</td></tr>' % (str(p), p.value()) for p in pins]
response=html % '\n'.join(rows)
cs.send(response)
cs.close()
s.close()
將此 myapp 上傳至 D1 Mini :
D:\test>ampy --port COM4 put myapp.py
按 Ctrl+D 軟開機, 然後手機連線 ESP8266 本身內建的 AP, 再用連覽器連線 192.168.4.1 便可看到 D1 Mini 各 GPIO 腳的狀態了 :
可見除 GPIO15 與 GPIO16 外, 其餘拉出來的 IO 腳都被 Pull-up 到 HIGH 位準了.
我覺得 D1 Mini 比 ESP-01 模組似乎更穩, 而且大小也沒有大多少, 價格也只貴了 20~30 元左右, 但 D1 Mini 有內建 CH340G USB 晶片與 Micro USB 插槽, 光是加這兩個元件就超過 30 元了, 而且還比 ESP-01 多出 7 個 GPIO 埠與 1 個 ADC 埠, 最重要的是, Flash 記憶體大小 D1 Mini 是 ESP-01 的四倍 (=4MB), 便利性與功能性都比 ESP-01 模組強. 如果是簡單的物聯網應用, GPIO 需求不超過 2 個的話, 用 ESP-01 即可; 否則用 D1 Mini 也沒多多少成本.
除了 D1 Mini 外, WeMOS 還有 D1 Mini Pro, Lite, 以及 Lolin 32 等開發板, 參考 :
# https://wiki.wemos.cc/products:d1:d1_mini_lite
# https://wiki.wemos.cc/products:d1:d1_mini
# https://wiki.wemos.cc/products:d1:d1_mini_pro
# https://wiki.wemos.cc/products:lolin32:lolin32
Lite, Pro 板子大小與 Mini 大小一樣, 但 Lite 的 Flash 記憶體只有 1 MB, 而 Pro 則是 16MB; Lite 沒使用 ESP 模組, 而是將 ESP8266 與天線直接佈在單一板子上. Pro 則是改用陶瓷天線並附有外接天線座, 同時 USB-TTL 晶片改為 CP2102. Lolin 特點是內建藍芽並提供鋰電池接頭 (使用 PH-2 2.0mm), Flash 則維持 4MB.
2017-07-30 補充 :
D1 Mini 天線底下有一顆藍色 LED, 開機送電時會閃一下, 根據上面的 GPIO 針腳對應表, 其中 D4 針腳 (GPIO 2) 有接 LED, 指的就是這顆 LED, 在開發物聯網應用時可以利用這個 LED 來顯示系統狀態, 例如 WiFi 尚未連線時快閃, 連線成功後常亮或慢閃; 系統異常時快閃等等, 這是使用 D1 Mini 的一個方便之處 : 不需外接 LED.
2017-08-02 補充 :
D1 Mini 也可以使用 Arduino 開發, 參考 :
# Wemos D1 Mini, ESP8266 Getting Started Guide With Arduino
另外跟同一賣家買的 Wemos NodeMCU V2 板子只是先焊好, 開機接上 COM 埠有顯示 Ready 表示板子 OK 即可, 賣家說預先搭載了 NodeMCU, 因此暫時不燒錄 MicroPython 進去, 等 MicroPython 弄完有空或許會研究看看, 參考 :
# Comparison of ESP8266 NodeMCU development boards
# TOP 6 ESP8266 MODULES FOR IOT PROJECTS
# Mini NodeMCU (Wemos), Prototype Board, NodeMCU (DOIT&LoLin), Arduino like Wemos, Witty
2017-08-14 補充 :
如果 D1 Mini 有接 1602 LCD 或 SSD1306 OLED 顯示器, 在開機過程中可以將連線 AP 的情況顯示於 LCD 或 OLED 上, 參考 :
# MicroPython on ESP8266 (十七) : 液晶顯示器 1602A 測試
# MicroPython on ESP8266 (十八) : SSD1306 液晶顯示器測試
下面列出無顯示器版, 1602 LCD 版, 以及 SSD1306 OLED 版的 AP 設定程式 main.py :
1. 無顯示器版 AP 設定程式 :
#main.py
from machine import Pin,PWM
import network
import time
WAIT_FOR_CONNECT=8
pwm2=PWM(Pin(2), freq=5, duty=512)
def set_ap():
html="""
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
%s
</body>
</html>
"""
form="""
<form method=get action='/update_ap'>
<table border="0">
<tr>
<td>SSID</td>
<td><input name=ssid type=text></td>
</tr>
<tr>
<td>PWD </td>
<td><input name=pwd type=text></td>
</tr>
<tr>
<td></td>
<td align=right><input type=submit value=Connect></td>
</tr>
</table>
</form>
"""
import socket
addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
s=socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
while True:
cs, addr=s.accept()
print('Client connected from', addr)
data=cs.recv(1024)
request=str(data,'utf8')
print(request, end='\n')
if request.find('update_ap?') == 5:
para=request[request.find('ssid='):request.find(' HTTP/')]
ssid=para.split('&')[0].split('=')[1]
pwd=para.split('&')[1].split('=')[1]
sta.connect(ssid,pwd)
while not sta.isconnected():
pass
print('Connected:IP=',sta.ifconfig()[0])
cs.send(html % 'Connected:IP=' + sta.ifconfig()[0])
else:
cs.send(html % form)
cs.close()
s.close()
def get_ip():
return (network.WLAN(network.STA_IF).ifconfig()[0],
network.WLAN(network.AP_IF).ifconfig()[0])
def ap_on():
network.WLAN(network.AP_IF).active(True)
def ap_off():
network.WLAN(network.AP_IF).active(False)
#try connecting to lastest configured AP
import network
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
set_ap()
else:
pwm2.deinit()
Pin(2).value(0)
print('Connected:IP=', sta.ifconfig()[0])
#Application code is written here or import from a separate file
import myapp
myapp.main()
2. 1602 LCD 版 AP 設定程式 :
#main.py
from machine import Pin,PWM,I2C
import network
import time
from esp8266_i2c_lcd import I2cLcd
WAIT_FOR_CONNECT=8
pwm2=PWM(Pin(2), freq=5, duty=512)
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
def set_ap():
html="""
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
%s
</body>
</html>
"""
form="""
<form method=get action='/update_ap'>
<table border="0">
<tr>
<td>SSID</td>
<td><input name=ssid type=text></td>
</tr>
<tr>
<td>PWD </td>
<td><input name=pwd type=text></td>
</tr>
<tr>
<td></td>
<td align=right><input type=submit value=Connect></td>
</tr>
</table>
</form>
"""
import socket
addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
s=socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
while True:
cs, addr=s.accept()
print('client connected from', addr)
data=cs.recv(1024)
request=str(data,'utf8')
print(request, end='\n')
if request.find('update_ap?') == 5:
para=request[request.find('ssid='):request.find(' HTTP/')]
ssid=para.split('&')[0].split('=')[1]
pwd=para.split('&')[1].split('=')[1]
sta.connect(ssid,pwd)
while not sta.isconnected():
pass
print('Connected:IP=',sta.ifconfig()[0])
cs.send(html % 'Connected:IP=' + sta.ifconfig()[0])
lcd.move_to(0, 0)
lcd.putstr('AP Connected')
lcd.move_to(0, 1)
lcd.putstr('IP=' + sta.ifconfig()[0])
else:
cs.send(html % form)
cs.close()
s.close()
def get_ip():
return (network.WLAN(network.STA_IF).ifconfig()[0],
network.WLAN(network.AP_IF).ifconfig()[0])
def ap_on():
network.WLAN(network.AP_IF).active(True)
def ap_off():
network.WLAN(network.AP_IF).active(False)
#try connecting to lastest configured AP
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
lcd.move_to(0, 0)
lcd.putstr('Connecting to AP')
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
set_ap()
else:
pwm2.deinit()
Pin(2).value(0)
print('Connected:IP=', sta.ifconfig()[0])
lcd.clear()
lcd.putstr('AP Connected')
lcd.move_to(0, 1)
lcd.putstr(sta.ifconfig()[0])
time.sleep(5)
#Application code is written here or import from a separate file
import myapp
myapp.main()
3. SSD1306 OLED 版 AP 設定程式 :
#main.py
from machine import Pin,PWM,I2C
import network
import time
import ssd1306
WAIT_FOR_CONNECT=8
pwm2=PWM(Pin(2), freq=5, duty=512)
i2c=I2C(scl=Pin(5), sda=Pin(4))
oled=ssd1306.SSD1306_I2C(128, 32, i2c)
def set_ap():
html="""
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
%s
</body>
</html>
"""
form="""
<form method=get action='/update_ap'>
<table border="0">
<tr>
<td>SSID</td>
<td><input name=ssid type=text></td>
</tr>
<tr>
<td>PWD </td>
<td><input name=pwd type=text></td>
</tr>
<tr>
<td></td>
<td align=right><input type=submit value=Connect></td>
</tr>
</table>
</form>
"""
import socket
addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
s=socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
oled.fill(0)
oled.text('Listening on :',0,0)
oled.text(addr[0],0,8)
oled.show()
while True:
cs, addr=s.accept()
print('client connected from', addr)
data=cs.recv(1024)
request=str(data,'utf8')
print(request, end='\n')
if request.find('update_ap?') == 5:
para=request[request.find('ssid='):request.find(' HTTP/')]
ssid=para.split('&')[0].split('=')[1]
pwd=para.split('&')[1].split('=')[1]
sta.connect(ssid,pwd)
while not sta.isconnected():
pass
print('Connected:IP=',sta.ifconfig()[0])
cs.send(html % 'Connected:IP=' + sta.ifconfig()[0])
oled.fill(0)
oled.text('AP connected',0,0)
oled.text(sta.ifconfig()[0],0,1)
oled.show()
else:
cs.send(html % form)
cs.close()
s.close()
def get_ip():
return (network.WLAN(network.STA_IF).ifconfig()[0],
network.WLAN(network.AP_IF).ifconfig()[0])
def ap_on():
network.WLAN(network.AP_IF).active(True)
def ap_off():
network.WLAN(network.AP_IF).active(False)
#try connecting to lastest configured AP
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
oled.text('Connecting to AP',0,0)
oled.show()
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
set_ap()
else:
pwm2.deinit()
Pin(2).value(0)
print('Connected:IP=', sta.ifconfig()[0])
oled.fill(0)
oled.text('AP connected',0,0)
oled.text(sta.ifconfig()[0],0,8)
oled.show()
time.sleep(5)
#Application code is written here or import from a separate file
import myapp
myapp.main()
而應用程式 myapp.py 的模板如下 :
#myapp.py
def main():
#application codes are placed here
print('Hello World!')
if __name__ == "__main__":
main()
也可以加上讓板上 LED (GPIO 2) 閃爍的功能 :
#myapp.py
from machine import Pin,PWM
def main():
#application codes are placed here
pwm2=PWM(Pin(2), freq=1, duty=512)
if __name__ == "__main__":
main()
上面這兩個模板在清除 UART 0 方面會用到, 因為程式若佔住 UART 0, 則 REPL 將無法使用, 上傳上面這兩個模板蓋掉使用 UART 0 的 myapp.py 即可讓 REPL 恢復可用狀態, 參考 :
# MicroPython on ESP8266 (二十二) : UART 串列埠測試
注意, 使用顯示器版的 main.py 需下載驅動程式, 1602 LCD 需要下載下列兩個程式 :
# https://github.com/dhylands/python_lcd/blob/master/lcd/lcd_api.py
# https://github.com/dhylands/python_lcd/blob/master/lcd/esp8266_i2c_lcd.py
SSD1306 OLED 版需下載下列程式 :
# https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py
將這些驅動程式與 main.py 一起上傳到 D1 Mini 即可.
D:\ESP8266\test>ampy --port COM8 put main.py
D:\ESP8266\test>ampy --port COM8 put cd_api.py
D:\ESP8266\test>ampy --port COM8 put esp8266_i2c_lcd.py
D:\ESP8266\test>ampy --port COM8 put ssd1306.py
然後按 Flash 鈕即會看到 ESP8266 的 MAC 位址顯示並開始燒錄, 過程中 D1 Mini 板上的藍色 LED 燈會閃爍, 表示韌體正寫入 Flash 中, 完成後左下角的 NODEMCU TEAM 前面會打勾 :
注意, ESP-12 系列的 Flash 記憶體都是 4MB.
韌體燒錄說明參見 :
# MicroPython on ESP8266 (一) : 燒錄韌體
# MicroPython v1.9.1 版韌體測試
燒錄完成後拔除 D3 (GPIO0) 的接地線重開機 (按 Reset 鈕或插拔 USB), 開啟 PuTTY 用 Serial 速率 115200 連線 D1 Mini, 按 Ctrl+B 即進入 REPL 介面, 用 os.listdir() 可知檔案目錄下面只有一個 boot.py 檔案 :
MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
>>> import os
>>> os.listdir()
['boot.py']
>>> f=open('boot.py','r')
>>> lines=f.readlines()
>>> for line in lines:
... print(line)
...
...
...
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
import gc
#import webrepl
#webrepl.start()
gc.collect()
>>>
然後開啟 Web REPL 功能 :
>>> import webrepl_setup
WebREPL daemon auto-start status: disabled
Would you like to (E)nable or (D)isable it running on boot?
(Empty line to quit)
> E (開啟 webrepl 功能)
To enable WebREPL, you must set password for it
New password: 123456
Confirm password: 123456
Changes will be activated after reboot
Would you like to reboot now? (y/n) Y
Would you like to reboot now? (y/n) y (要用小寫 y)
l▒▒|▒▒#4 ets_task(40100164, 3, 3fff829c, 4)
WebREPL daemon started on ws://192.168.4.1:8266
Started webrepl in normal mode
OSError: [Errno 2] ENOENT
MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
更多檔案操作參考 :
# MicroPython on ESP8266 (六) : 檔案系統測試
接下來參考之前伺服器測試這篇 :
# MicroPython on ESP8266 (十四) : 網頁伺服器測試
用記事本編輯如下 main.py 檔案 :
#main.py
import time
WAIT_FOR_CONNECT=8
def set_ap():
html="""
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
%s
</body>
</html>
"""
form="""
<form method=get action='/update_ap'>
<table border="0">
<tr>
<td>SSID</td>
<td><input name=ssid type=text></td>
</tr>
<tr>
<td>PWD </td>
<td><input name=pwd type=text></td>
</tr>
<tr>
<td></td>
<td align=right><input type=submit value=Connect></td>
</tr>
</table>
</form>
"""
import socket
addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
s=socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
while True:
cs, addr=s.accept()
print('client connected from', addr)
data=cs.recv(1024)
request=str(data,'utf8')
print(request, end='\n')
if request.find('update_ap?') == 5:
para=request[request.find('ssid='):request.find(' HTTP/')]
ssid=para.split('&')[0].split('=')[1]
pwd=para.split('&')[1].split('=')[1]
sta.connect(ssid,pwd)
while not sta.isconnected():
pass
print('Connected:IP=',sta.ifconfig()[0])
cs.send(html % 'Connected:IP=' + sta.ifconfig()[0])
else:
cs.send(html % form)
cs.close()
s.close()
import network
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
set_ap()
else:
print('Connected:IP=', sta.ifconfig()[0])
#Application code is written here or import from a separate file
#import myapp
#myapp.main()
存成 main.py 後用 ampy 將其上傳到 D1 Mini 裡 :
D:\test>ampy --port COM4 put main.py
然後按 Ctrl+D 軟開機 :
PYB: soft reboot
#9 ets_task(40100164, 3, 3fff829c, 4)
WebREPL is not configured, run 'import webrepl_setup'
Connecting to AP ... (連線到最近一次設定之無線基地台, 等候 8 秒)
Connected:IP= 192.168.2.111 (連線成功)
MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
>>>
如果顯示 "listening on 192.168.4.1", 表示上次設定的 AP 目前收不到訊號無法連線, 這時打開手機 WiFi 功能, 找尋 D1 Mini 的 ESP8266 內建的 AP, 其 SSID 是以 "MicroPython-" 開頭 (後面是 MAC 後 6 碼), 以內建密碼 "micropythoN" 連線此 AP :
SSID=MicroPython-c111d1
PWD=micropythoN
再打開手機瀏覽器連線 192.168.4.1, 會看到一個 AP 設定網頁, 輸入欲連線之 AP 的 SSID 與 PWD 後按 Connect 即可, 這時 D1 Mini 重開機即可連線至該無線基地台了.
接著修改上面 main.py 的最後兩行, 拿掉前面的 #, 變成 :
#myapp.py
import myapp
myapp.main()
重新上傳 main.py, 然後參考下面這篇的測試 4 : "建立網頁伺服器顯示 GPIO 輸入腳的狀態" :
# MicroPython on ESP8266 (十四) : 網頁伺服器測試
#myapp.py
import machine
pins=[machine.Pin(i, machine.Pin.IN) for i in (0, 2, 4, 5, 12, 13, 14, 15, 16)]
html="""<!DOCTYPE html>
<html>
<head> <title>ESP8266 Pins</title> </head>
<body> <h1>ESP8266 Pins</h1>
<table border="1"> <tr><th>Pin</th><th>Value</th></tr> %s </table>
</body>
</html>
"""
import socket
addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
s=socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
while True:
cs, addr=s.accept()
print('client connected from', addr)
data=cs.recv(1024)
print(str(data,'utf8'), end='\n')
rows=['<tr><td>%s</td><td>%d</td></tr>' % (str(p), p.value()) for p in pins]
response=html % '\n'.join(rows)
cs.send(response)
cs.close()
s.close()
D:\test>ampy --port COM4 put myapp.py
按 Ctrl+D 軟開機, 然後手機連線 ESP8266 本身內建的 AP, 再用連覽器連線 192.168.4.1 便可看到 D1 Mini 各 GPIO 腳的狀態了 :
可見除 GPIO15 與 GPIO16 外, 其餘拉出來的 IO 腳都被 Pull-up 到 HIGH 位準了.
我覺得 D1 Mini 比 ESP-01 模組似乎更穩, 而且大小也沒有大多少, 價格也只貴了 20~30 元左右, 但 D1 Mini 有內建 CH340G USB 晶片與 Micro USB 插槽, 光是加這兩個元件就超過 30 元了, 而且還比 ESP-01 多出 7 個 GPIO 埠與 1 個 ADC 埠, 最重要的是, Flash 記憶體大小 D1 Mini 是 ESP-01 的四倍 (=4MB), 便利性與功能性都比 ESP-01 模組強. 如果是簡單的物聯網應用, GPIO 需求不超過 2 個的話, 用 ESP-01 即可; 否則用 D1 Mini 也沒多多少成本.
項目 | ESP-01 模組 | WeMOS D1 Mini |
價格 | 約 80 元 | 約 110 元 |
GPIO 數 | 2 | 9 |
Flash 大小 | 1M Bytes | 4M Bytes |
ADC 腳 | 無 | 有 1 個 |
USB 介面 | 無 (須外接轉換線) | Micro USB |
Reset 鈕 | 無 | 有 |
除了 D1 Mini 外, WeMOS 還有 D1 Mini Pro, Lite, 以及 Lolin 32 等開發板, 參考 :
# https://wiki.wemos.cc/products:d1:d1_mini_lite
# https://wiki.wemos.cc/products:d1:d1_mini
# https://wiki.wemos.cc/products:d1:d1_mini_pro
# https://wiki.wemos.cc/products:lolin32:lolin32
Lite, Pro 板子大小與 Mini 大小一樣, 但 Lite 的 Flash 記憶體只有 1 MB, 而 Pro 則是 16MB; Lite 沒使用 ESP 模組, 而是將 ESP8266 與天線直接佈在單一板子上. Pro 則是改用陶瓷天線並附有外接天線座, 同時 USB-TTL 晶片改為 CP2102. Lolin 特點是內建藍芽並提供鋰電池接頭 (使用 PH-2 2.0mm), Flash 則維持 4MB.
WeMOS | D1 Mini Lite | D1 Mini | D1 Mini Pro | Lolin |
微控器 | ESP-8285 | ESP-8266EX | ESP-8266EX | ESP-WROOM-32 |
最高時脈 | 80/160MHz | 80/160MHz | 80/160MHz | 240MHz |
記憶體大小 | 1MB | 4MB | 16MB | 4MB |
天線 | PCB | PCB | 陶瓷 + 外接座 | PCB |
藍芽 | No | No | No | Yes |
USB-TTL | CH340G | CH340G | CH340G | CP2102 |
鋰電池接頭 | No | No | No | Yes |
2017-07-30 補充 :
D1 Mini 天線底下有一顆藍色 LED, 開機送電時會閃一下, 根據上面的 GPIO 針腳對應表, 其中 D4 針腳 (GPIO 2) 有接 LED, 指的就是這顆 LED, 在開發物聯網應用時可以利用這個 LED 來顯示系統狀態, 例如 WiFi 尚未連線時快閃, 連線成功後常亮或慢閃; 系統異常時快閃等等, 這是使用 D1 Mini 的一個方便之處 : 不需外接 LED.
2017-08-02 補充 :
D1 Mini 也可以使用 Arduino 開發, 參考 :
# Wemos D1 Mini, ESP8266 Getting Started Guide With Arduino
另外跟同一賣家買的 Wemos NodeMCU V2 板子只是先焊好, 開機接上 COM 埠有顯示 Ready 表示板子 OK 即可, 賣家說預先搭載了 NodeMCU, 因此暫時不燒錄 MicroPython 進去, 等 MicroPython 弄完有空或許會研究看看, 參考 :
# Comparison of ESP8266 NodeMCU development boards
# TOP 6 ESP8266 MODULES FOR IOT PROJECTS
# Mini NodeMCU (Wemos), Prototype Board, NodeMCU (DOIT&LoLin), Arduino like Wemos, Witty
2017-08-14 補充 :
如果 D1 Mini 有接 1602 LCD 或 SSD1306 OLED 顯示器, 在開機過程中可以將連線 AP 的情況顯示於 LCD 或 OLED 上, 參考 :
# MicroPython on ESP8266 (十七) : 液晶顯示器 1602A 測試
# MicroPython on ESP8266 (十八) : SSD1306 液晶顯示器測試
下面列出無顯示器版, 1602 LCD 版, 以及 SSD1306 OLED 版的 AP 設定程式 main.py :
1. 無顯示器版 AP 設定程式 :
#main.py
from machine import Pin,PWM
import network
import time
WAIT_FOR_CONNECT=8
pwm2=PWM(Pin(2), freq=5, duty=512)
def set_ap():
html="""
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
%s
</body>
</html>
"""
form="""
<form method=get action='/update_ap'>
<table border="0">
<tr>
<td>SSID</td>
<td><input name=ssid type=text></td>
</tr>
<tr>
<td>PWD </td>
<td><input name=pwd type=text></td>
</tr>
<tr>
<td></td>
<td align=right><input type=submit value=Connect></td>
</tr>
</table>
</form>
"""
import socket
addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
s=socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
while True:
cs, addr=s.accept()
print('Client connected from', addr)
data=cs.recv(1024)
request=str(data,'utf8')
print(request, end='\n')
if request.find('update_ap?') == 5:
para=request[request.find('ssid='):request.find(' HTTP/')]
ssid=para.split('&')[0].split('=')[1]
pwd=para.split('&')[1].split('=')[1]
sta.connect(ssid,pwd)
while not sta.isconnected():
pass
print('Connected:IP=',sta.ifconfig()[0])
cs.send(html % 'Connected:IP=' + sta.ifconfig()[0])
else:
cs.send(html % form)
cs.close()
s.close()
def get_ip():
return (network.WLAN(network.STA_IF).ifconfig()[0],
network.WLAN(network.AP_IF).ifconfig()[0])
def ap_on():
network.WLAN(network.AP_IF).active(True)
def ap_off():
network.WLAN(network.AP_IF).active(False)
#try connecting to lastest configured AP
import network
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
set_ap()
else:
pwm2.deinit()
Pin(2).value(0)
print('Connected:IP=', sta.ifconfig()[0])
#Application code is written here or import from a separate file
import myapp
myapp.main()
2. 1602 LCD 版 AP 設定程式 :
#main.py
from machine import Pin,PWM,I2C
import network
import time
from esp8266_i2c_lcd import I2cLcd
WAIT_FOR_CONNECT=8
pwm2=PWM(Pin(2), freq=5, duty=512)
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
def set_ap():
html="""
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
%s
</body>
</html>
"""
form="""
<form method=get action='/update_ap'>
<table border="0">
<tr>
<td>SSID</td>
<td><input name=ssid type=text></td>
</tr>
<tr>
<td>PWD </td>
<td><input name=pwd type=text></td>
</tr>
<tr>
<td></td>
<td align=right><input type=submit value=Connect></td>
</tr>
</table>
</form>
"""
import socket
addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
s=socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
while True:
cs, addr=s.accept()
print('client connected from', addr)
data=cs.recv(1024)
request=str(data,'utf8')
print(request, end='\n')
if request.find('update_ap?') == 5:
para=request[request.find('ssid='):request.find(' HTTP/')]
ssid=para.split('&')[0].split('=')[1]
pwd=para.split('&')[1].split('=')[1]
sta.connect(ssid,pwd)
while not sta.isconnected():
pass
print('Connected:IP=',sta.ifconfig()[0])
cs.send(html % 'Connected:IP=' + sta.ifconfig()[0])
lcd.move_to(0, 0)
lcd.putstr('AP Connected')
lcd.move_to(0, 1)
lcd.putstr('IP=' + sta.ifconfig()[0])
else:
cs.send(html % form)
cs.close()
s.close()
def get_ip():
return (network.WLAN(network.STA_IF).ifconfig()[0],
network.WLAN(network.AP_IF).ifconfig()[0])
def ap_on():
network.WLAN(network.AP_IF).active(True)
def ap_off():
network.WLAN(network.AP_IF).active(False)
#try connecting to lastest configured AP
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
lcd.move_to(0, 0)
lcd.putstr('Connecting to AP')
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
set_ap()
else:
pwm2.deinit()
Pin(2).value(0)
print('Connected:IP=', sta.ifconfig()[0])
lcd.clear()
lcd.putstr('AP Connected')
lcd.move_to(0, 1)
lcd.putstr(sta.ifconfig()[0])
time.sleep(5)
#Application code is written here or import from a separate file
import myapp
myapp.main()
3. SSD1306 OLED 版 AP 設定程式 :
#main.py
from machine import Pin,PWM,I2C
import network
import time
import ssd1306
WAIT_FOR_CONNECT=8
pwm2=PWM(Pin(2), freq=5, duty=512)
i2c=I2C(scl=Pin(5), sda=Pin(4))
oled=ssd1306.SSD1306_I2C(128, 32, i2c)
def set_ap():
html="""
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
%s
</body>
</html>
"""
form="""
<form method=get action='/update_ap'>
<table border="0">
<tr>
<td>SSID</td>
<td><input name=ssid type=text></td>
</tr>
<tr>
<td>PWD </td>
<td><input name=pwd type=text></td>
</tr>
<tr>
<td></td>
<td align=right><input type=submit value=Connect></td>
</tr>
</table>
</form>
"""
import socket
addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
s=socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
oled.fill(0)
oled.text('Listening on :',0,0)
oled.text(addr[0],0,8)
oled.show()
while True:
cs, addr=s.accept()
print('client connected from', addr)
data=cs.recv(1024)
request=str(data,'utf8')
print(request, end='\n')
if request.find('update_ap?') == 5:
para=request[request.find('ssid='):request.find(' HTTP/')]
ssid=para.split('&')[0].split('=')[1]
pwd=para.split('&')[1].split('=')[1]
sta.connect(ssid,pwd)
while not sta.isconnected():
pass
print('Connected:IP=',sta.ifconfig()[0])
cs.send(html % 'Connected:IP=' + sta.ifconfig()[0])
oled.fill(0)
oled.text('AP connected',0,0)
oled.text(sta.ifconfig()[0],0,1)
oled.show()
else:
cs.send(html % form)
cs.close()
s.close()
def get_ip():
return (network.WLAN(network.STA_IF).ifconfig()[0],
network.WLAN(network.AP_IF).ifconfig()[0])
def ap_on():
network.WLAN(network.AP_IF).active(True)
def ap_off():
network.WLAN(network.AP_IF).active(False)
#try connecting to lastest configured AP
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
oled.text('Connecting to AP',0,0)
oled.show()
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
set_ap()
else:
pwm2.deinit()
Pin(2).value(0)
print('Connected:IP=', sta.ifconfig()[0])
oled.fill(0)
oled.text('AP connected',0,0)
oled.text(sta.ifconfig()[0],0,8)
oled.show()
time.sleep(5)
#Application code is written here or import from a separate file
import myapp
myapp.main()
而應用程式 myapp.py 的模板如下 :
#myapp.py
def main():
#application codes are placed here
print('Hello World!')
if __name__ == "__main__":
main()
也可以加上讓板上 LED (GPIO 2) 閃爍的功能 :
#myapp.py
from machine import Pin,PWM
def main():
#application codes are placed here
pwm2=PWM(Pin(2), freq=1, duty=512)
if __name__ == "__main__":
main()
上面這兩個模板在清除 UART 0 方面會用到, 因為程式若佔住 UART 0, 則 REPL 將無法使用, 上傳上面這兩個模板蓋掉使用 UART 0 的 myapp.py 即可讓 REPL 恢復可用狀態, 參考 :
# MicroPython on ESP8266 (二十二) : UART 串列埠測試
注意, 使用顯示器版的 main.py 需下載驅動程式, 1602 LCD 需要下載下列兩個程式 :
# https://github.com/dhylands/python_lcd/blob/master/lcd/lcd_api.py
# https://github.com/dhylands/python_lcd/blob/master/lcd/esp8266_i2c_lcd.py
SSD1306 OLED 版需下載下列程式 :
# https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py
將這些驅動程式與 main.py 一起上傳到 D1 Mini 即可.
D:\ESP8266\test>ampy --port COM8 put main.py
D:\ESP8266\test>ampy --port COM8 put cd_api.py
D:\ESP8266\test>ampy --port COM8 put esp8266_i2c_lcd.py
D:\ESP8266\test>ampy --port COM8 put ssd1306.py
13 則留言 :
請問進行載入boot.py這個動作運行等待時間約為多久 我已經等了1小時 除了晶片發燙之外沒有特別的反應 晶片型號是esp8266MOD
燒錄完韌體裡面就已經有 boot.py 了, "載入 boot.py" 是指用 ampy 工具傳檔嗎? ampy 傳檔必須先將 Putty 連線關閉.
mini 可以作用 wifi_repeater嗎??
應該可以, 參考 github :
https://github.com/martin-ger/esp_wifi_repeater
用家用pppoe 可以在d1 上掛ddns嗎??
有看到一個eayddns.h ,但是試了開啓
Arduino的監視只是空白 ,若把eayddns.h 刪掉,用wifi 是ok,也有ip
還有其他方法掛ddns嗎??
Sorry, DDNS 我倒是沒用過呢! 基本上我是以樹莓派當物聯網的 Gateway, 靠 WiFi 收集 ESP8266 這些終端的訊息, 而樹莓派我是利用 Shell 程式與 crontab 定時檢查外網 ip 有異動時發信通知來從外網控制樹莓派, 從而控制 esp8266.
請問d1可以直接使用arduino library內建的softwareserial? 我使用起來好像不能編譯
之前有試過不行, 但後來改用 MicroPython 就沒進一步研究了.
請問為什麼我的MAC一直沒辦法裝載驅動程式? 我接上電腦後D1 mini只是閃一下沒有一直閃是正常的嗎?
抱歉, 我沒在 Mac OS 上跑過, 不過您可參考下面這篇安裝 D1 mini 的驅動程式
https://medium.com/salted-bytes/getting-the-wemos-d1-mini-board-to-work-on-mac-os-10-13-high-sierra-f30324d82db2
感謝你的文章,收穫很多
謝謝, 多交流!
您好請問版主我有一個數位功率計(baudrate 4800)用D1 Mini去偵測,再利用WiFi回傳至網頁,請問是因為baudrate問題導致無法傳輸嗎?
我的架構
電腦
D1 Mini
數位功率計(具TXRX功能)
負載
張貼留言