2017年9月3日 星期日

MicroPython on ESP8266 (二十二) : UART 串列埠測試

做完 Twitter 測試後, 接下來是研究如何使用 MicroPython on ESP8266 進行 UART 串列通訊. 關於 UART 可參考去年做 Arduino 的 UART 測試時所留下的紀錄, 當時是使用兩塊 Arduino Nano 板子以 UART 互傳資料 :

Arduino 串列埠測試 (UART)

在以下的測試中, 我使用一塊 D1 Mini 的 ESP8266 模組與 Arduino Nano 進行 UART 通訊, D1 Mini 有一組 UART埠 (TX/RX), 而 Arduino Nano 只有一組硬體 UART 埠且與 USB 共用, 因此無法使用這組硬體 UART 與 D1 Mini 相連接, 因為我們用來觀察串列埠資訊送收情形的串列埠監視視窗就是使用此 TX/RX 與 Arduino 通訊, 因此必須指定 Arduino 的兩個 DIO 腳當作軟體序列埠與 D1 Mini 通訊.

另外, 由於我使用的 Nano 是 5V 系統的板子, 所以 D1 Mini 與 Arduino 的 UART 之間需透過位準轉換電路介接 5V 與 3.3V 系統. 這有兩種做法, 第一種是純粹使用被動元件-即電阻分壓法來處理 5V -> 3.3V 轉換, 可用 1K+2K 歐姆串聯電路接到 Arduino 軟體串列埠的 TX, 經過分壓後得到 5*2/3=3.33V 輸出給 D1 Mini 的 RX. 反方向的 D1 Mini 的 TX 可直接接到 Arduino 軟體串列埠的 RX, 因為 3.3V 在 TTL 邏輯仍然被視為 High, 只是抗雜訊邊限少了 1.7V 而已. 電路圖如下 :


第二種方法是使用主動元件 MOSFET (例如 2N7000) 與電阻做成的雙向位準轉換器, 參考 :

用 MOFFET 2N7000 做 5V 與 3.3V 位準轉換

也可以購買現成的雙向位準轉換模組, 例如我上次在露天購買的這塊才 10 元, 很便宜 :

4路電平轉換模組 雙向互轉 3.3V 轉 5V IIC I2C UART SPI $10

此模組具有 4 個轉換通道, 但此處只會用到 2 個, 電路圖如下 :


此模組的 LV 是低壓電源端, 接 D1 Mini 的 3.3V 輸出腳; 而 HV 是高壓電源端, 接 5V 腳.

本系列 MicroPython on ESP8266 測試文章參考 :

MicroPython on ESP8266 (一) : 燒錄韌體
MicroPython on ESP8266 (二) : 數值型別測試
MicroPython on ESP8266 (三) : 序列型別測試
MicroPython on ESP8266 (四) : 字典與集合型別測試
MicroPython on ESP8266 (五) : WiFi 連線與 WebREPL 測試
MicroPython on ESP8266 (六) : 檔案系統測試
MicroPython on ESP8266 (七) : 時間日期測試
MicroPython on ESP8266 (八) : GPIO 測試
MicroPython on ESP8266 (九) : PIR 紅外線移動偵測
MicroPython v1.9.1 版韌體測試
MicroPython on ESP8266 (十) : socket 模組測試
MicroPython on ESP8266 (十一) : urllib.urequest 模組測試
MicroPython on ESP8266 (十二) : urequests 模組測試
MicroPython on ESP8266 (十三) : DHT11 溫溼度感測器測試
MicroPython 使用 ampy 突然無法上傳檔案問題
MicroPython on ESP8266 (十四) : 網頁伺服器測試
WeMOS D1 Mini 開發板測試
MicroPython on ESP8266 (十五) : 光敏電阻與 ADC 測試
MicroPython on ESP8266 (十六) : 蜂鳴器測試
MicroPython on ESP8266 (十七) : 液晶顯示器 1602A 測試
MicroPython on ESP8266 (十八) : SSD1306 液晶顯示器測試
MicroPython v1.9.2 版釋出
MicroPython on ESP8266 (十九) : 太陽能測候站與移動偵測控制照明
MicroPython on ESP8266 (二十) : 從 ThingSpeak 讀取資料
MicroPython on ESP8266 (二十一) : 使用 ThingTweet 傳送推文

MicroPython 文件參考 :

MicroPython tutorial for ESP8266  (官方教學)
http://docs.micropython.org/en/latest/micropython-esp8266.pdf
http://docs.micropython.org/en/latest/pyboard/library/usocket.html#class-socket
http://docs.micropython.org/en/v1.8.7/esp8266/library/usocket.html#module-usocket
https://gist.github.com/xyb/9a4c8d7fba92e6e3761a (驅動程式)

我使用位準轉換模組 (上面第二張圖) 接好線後, 將下列 Arduino 程式上傳 :


#include <SoftwareSerial.h>
SoftwareSerial mySerial(11,10);  //建立軟體串列埠腳位 (RX, TX)
int LED=13;
int i=0;

void led_blink() {
  digitalWrite(LED, HIGH);
  delay(50);
  digitalWrite(LED, LOW);
  delay(50);
  }

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(9600);        //設定硬體串列埠速率
  mySerial.begin(9600);   //設定軟體串列埠速率
  }

void loop() {
  if (mySerial.available() > 0) {
    i=mySerial.read();
    Serial.write(i);
    }
  led_blink();
  }

這裡是將 Arduino 的 D10/D11 腳設為軟體串列埠的 RX/TX, 用這兩個腳與 D1 Mini 的 TX/RX 對接, 即 TX 接 D10, RX 接 D11. 此程式讓 Arduino 在迴圈中不斷地掃描軟體串列埠, 如果緩衝區有收到 D1 Mini 傳來的資料就將其輸出在串列埠監控視窗. 程式中還設了一個閃爍板上 LED 的函數讓我們知道 Arduino 有在跑.

然後用 ampy 刪除檔案系統下的 main.py, 只留 boot.py, 然後將 D1 Mini 接上 USB 開機, 打開 Putty 後進入 REPL 介面, 然後根據教學文件說明輸入下列指令 :

MicroPython v1.9.2-8-gbf8f45cf on 2017-08-23; ESP module with ESP8266
Type "help()" for more information.
>>> from machine import UART     #匯入 machine.UART 模組
>>> uart=UART(0,9600)                    #建構 UART 物件 (Bus 0)

結果到建構 UART 物件這步時整個 REPL 介面就卡住沒反應了, 連退出 Putty 重新上傳 main.py 都失敗, 試了好幾次都是如此, 只好重新燒錄韌體.

查了網路資料才發現原來 ESP8266 有兩組硬體 UART 埠 : UART(0) 與 UART(1), 其中 UART(0) 就是 ESP-12 模組或 D1 Mini 板上的 TX/RX 腳, 這 UART(0) 已經被 REPL 占用了, 因此在 REPL 介面上建立 UART(0)  會導致衝突而卡住. 而 UART(1) 雖然可以建立, 但是其 RX 已經被 SP1 Flash 占用, 只剩 TX 腳 (GPIO 15 或 D1 Mini 的 D8 腳) 可用.

在下面這篇文章中, 作者 deshipu 提到第二個 UART, 即 UART(1), 只有 TX 腳 (GPIO15, 在 D1 Mini 為 D8 腳), 沒有 RX 腳, 因此只能呼叫 write(), 不能呼叫 read() :

So you want to use the UART...

"Both uarts send diagnostic messages at startup, and the second UART only has a TX pin (no RX), so it's not that useful."

根據下面這篇所述, UART(0) (即 D1 Mini 板上的 TX/RX 腳) 與 USB 共接, 且被 REPL 拿去用了無法使用, 而且 UART(1) 也只能傳送不能接收資料, 參考 :

esp8266 uart 2 not available #2391

"There is UART 0 that is connected to the usb-serial converter and runs the repl.
There is UART 1 that only has a TX pin so I cannot receive data."

另外一篇也提到, UART(1) 是 Write-only, 無法讀取資料 :

READING SERIAL DATA WITH MICROPYTHON

"uart1 is write-only, you cannot read from it. Also, repl operates over uart0, so you cannot both interact with a device over uart0 and use repl at the same time."

下面這篇則較詳細提到 ESP8266 有兩個硬體 UART, UART 0 被 REPL 使用了, 而 UART 1 的 RX 被板上的 SPI Flash 寫入用掉了, 所以只有 TX 可用 :

https://forum.micropython.org/viewtopic.php?t=2204

"The ESP8266 has two hardware UARTs.
UART 0 - TX and RX is used by the REPL.
UART 1 - TX is free for use, however, RX is used by the onboard spi-flash."

我用 D1 Mini 測試確實如此 :

MicroPython v1.9.2-8-gbf8f45cf on 2017-08-23; ESP module with ESP8266
Type "help()" for more information.
>>> from machine import UART     #匯入 machine.UART 模組
>>> uart=UART(1,9600)                    #建構 UART 物件 (Bus 1)
>>> uart.init(9600, bits=8, parity=None, stop=1)
>>> uart.write("Hello \n")                   #UART(1) 有 TX, 可以寫入
7
>>> uart.any()
0
>>> val=uart.read()  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: UART(1) can't read                  #UART(1) 的 RX 無法使用, 不能讀取

既然只有 UART 1 的 TX (GPIO 15 或 D8) 可用, 於是我將 D1 Mini 的 D8 腳與 Arduino 軟體串列埠的 RX 對接, 然後在 REPL 下 uart.write('Hello World!') 指令卻沒在 Arduino 串列埠監控視窗看到任何訊息, Why? 其實就算 UART(1) 的 TX 可用, 那也是半吊子的 UART, 沒辦法接收資料. 我就想說 MicroPython on ESP8266 有沒有像 Arduino 那樣也有一個軟體串列埠模組呢?下面這篇文章說目前沒有, 要寫的話也不是很容易就搞定的, 例如 timing 問題 :

Software serial?

不過這篇提到可否改用 WebREPL 與 ESP8266 溝通,  將 UART(0) 的 TX/RX 釋放出來? 這倒是個好主意, 因為 WebREPL 是透過網路與 ESP8266 溝通, 或許不會跟 UART(0) 衝突. 關於 WebREPL 的用法參考 :

MicroPython on ESP8266 (五) : WiFi 連線與 WebREPL 測試

注意, 使用 WebREPL 之前務必先在 REPL 介面用 import webrepl_setup 指令啟動 WebREPL 功能並設定密碼, 否則在 WebREPL 網頁再怎麼按 Connect 鈕都不可能成功連線的 :

>>> 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)

這樣便啟動 WebREPL 功能了, 接下來要讓 ESP8266 透過 WiFi 基地台連上 Internet, 可參考下面這篇最後面所附的三種 main.py 連網程式, 在 REPL 介面用 ampy 將 main.py 上傳後重開機, 用手機的 Chrome 設定欲連線之基地台即可 :

WeMOS D1 Mini 開發板測試

ESP8266 連上 Internet 後, 記下取得之 IP, 例如 192.168.2.108, 按下列連結 :

http://micropython.org/webrepl/

把輸入框中預設之 ws://192.168.4.1:8266/ 改成 ws://192.168.2.108:8266/, 按 Connect 就會詢問 WebREPL 密碼, 驗證成功後即可對 ESP8266 下指令了:


Welcome to MicroPython!                                                                                
Password:                                                                                              
WebREPL connected                                                                          
>>> from machine import UART                                                                             
>>> uart=UART(0,9600)           #建立 UART(0)  物件                                          
>>> uart.init(9600,bits=8,parity=None,stop=1)                                                        
>>> uart.write('abcd')                                                                                     
4     #傳回字元數                                                
>>> uart.write('Hello World!')                                                                         
12   #傳回字元數                                                                                                
>>>



Arduino 串列埠監控視窗輸出如下 :

>>> uart.write(b'abcd')
abcd4
>>> uart.write('Hello World!)
Hello World!12

可見透過 WebREPL 下的指令與回應都會經網路送到 TX/RX, 所以 Arduino 串列埠監控視窗會收到完整的指令與回應. 如果是在執行的 main.py 程式中操作 UART 的話就不會出現 WebREPL 的操作指令了, 例如下列測試 1 :

測試 1 : ESP8266 向 Arduino 透過 UART 傳送 'Hello World!'

Arduino 部分為了 Timing 考量, 我拿掉 blink_led() 函數, 改為 delay(1), 因為 blink_led() 延遲太久會使部分傳送過來的 byte 漏掉 (緩衝區爆掉) :

#include <SoftwareSerial.h>
SoftwareSerial sSerial(11,10);  //建立軟體串列埠腳位 (RX, TX)
int LED=13;
int i=0;

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(9600);        //設定硬體串列埠速率
  sSerial.begin(9600);      //設定軟體串列埠速率
  }

void loop() {
  if (sSerial.available() > 0) {   #軟體串列埠 RX 有收到資料
    i=sSerial.read();
    Serial.write(i);
    }
  delay(1);
  }

而 ESP8266 的程式如下 :

#myapp.py
import time
from machine import UART

uart=UART(0,9600)
uart.init(9600, bits=8, parity=None, stop=1)

while True:
    uart.write('Hello World!\n')
    time.sleep(1)

將以上程式利用 WebREPL 頁面右邊的 "Send a file" 功能上傳到 ESP8266 :


重開機即可看到 Arduino 串列埠監控每秒輸出一個 "Hello World!" 了 :


前面的亂碼是 D1 Mini 開機時 TX/RX 上的隨機訊息. 注意, 只要程式開始向 TX/RX 傳送資料, 就無法用 ampy 上傳資料了, 因為 TX/RX 跟 USB 埠接在一起, 當 ampy 透過 USB 上傳資料時就會跟執行中的程式衝突, 因為無法取得回應而卡住了, 只能透過 WebREPL 上傳更新程式.

其實 WebREPL 也是一樣, 只要 MicroPython 程式開始使用 TX/RX 腳, 登入 WebREPL 後指令行就卡住了, 無法輸入指令, 只有右上角上傳下載檔案還能用, 可以說只要程式占用了 UART(0), 基本上不管 REPL 還是 WebREPL 都沒辦法用, 但 WebREPL 比 REPL 好的是, 至少它還可以上傳新的 main.py 或 myapp.py 終止程式使用 TX/RX, 如此 ampy 與 REPL 才能恢復可用, 例如下面這個 myapp.py 就可以用來蓋掉含有 UART(0) 操作的 myapp.py :

#myapp.py
def main():
    #application codes are placed here
    print('Hello World!')

if __name__ == "__main__":
    main()

接下來我想參考 "Arduino 串列埠測試 (UART)" 這篇裡的測試 5, 讓 ESP8266 與 Arduino 透過 UART 互丟 'Y' 字元, 收到後就將自己板上 LED 閃兩下, 然後回傳 'Y' 給對方, 如此反覆不斷來回傳遞.

測試 2 : ESP8266 與 Arduino 透過 UART 交替傳送 'Y' 給對方


Arduino 部分程式如下 :

#include <SoftwareSerial.h>
SoftwareSerial sSerial(11,10);  //建立軟體串列埠腳位 (RX, TX)
int LED=13;

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(9600);        //設定硬體串列埠速率
  sSerial.begin(9600);      //設定軟體串列埠速率
  }

void loop() {
  while (!sSerial.available()) {}  //等到 ESP8266 傳送字元才到下一步
  if (sSerial.read()=='Y') {         //等到軟體串列埠 RX 收到 ESP8266 傳來 'Y' 字元
    LED_blink(2);
    Serial.println("Got 'Y'!");       //向 PC 傳送字串
    sSerial.write('Y');                   //向 ESP8266 傳送 'Y'
    }
  }

void LED_blink(int c) {
  for (int i=0; i<c; i++) {
    digitalWrite(LED, HIGH);
    delay(500);
    digitalWrite(LED, LOW);
    delay(500);
    }
  }

Arduino 一開機是扮演被動角色, 等待 ESP8266 傳送 'Y' 過來, 一旦收到 'Y' 就將板上的 LED 閃爍兩次, 然後用軟體串列埠對 ESP8266 回送 'Y'.

ESP8266 部分的 MicroPython 程式如下, ESP8266 一開機是扮演主動角色, 先向 Arduino 送出初始的 'Y' 字元 :

import time
from machine import UART, Pin

def LED_blink(pin,s):
    for i in range(0,s):
        pin.value(1)
        time.sleep_ms(500)
        pin.value(0)
        time.sleep_ms(500)

LEDPIN=Pin(2, Pin.OUT)

uart=UART(0,9600)
uart.init(9600, bits=8, parity=None, stop=1)     #初始化 UART
uart.write('Y')                              #向 Arduino 送出初始 'Y'

while True:
    if uart.any():                            #RX 緩衝器有收到資料
        val=uart.read()                    #讀取 RX 緩衝器全部內容
        if val.find(b"Y") != -1:        #搜尋是否有收到 'Y'
            LED_blink(LEDPIN,2)   #板上 LED 閃兩次
            uart.write('Y')                  #向 Arduino 傳送 'Y'
    time.sleep_ms(1)


此程式在無限迴圈中先呼叫 any() 方法來檢查 UART 接收緩衝器是否有收到 Arduino 傳來的資料, 有的話會傳回已收到之 Bytes 數, 否則傳回 0. 有收到資料就呼叫 read() 方法讀取緩衝器, 其傳回值為 Bytes 物件, 此方法會讀取全部緩衝器收到之資料. 可用 Bytes 物件之 find() 方法尋找是否有收到特定字元序列. 注意, 由於讀取到的是 Bytes 物件, 因此須比對 b'Y' 而不是 'Y'. 若比對成功就讓板上 LED 閃兩下, 然後向 ESP8266 送出 'Y'.

由於無法從 REPL 觀察程式執行過程, 因此我另外給 ESP8266 接上 SSD1306 OLED 顯示器來輸出執行中狀態, 關於 SSD1306 用法參考 :

MicroPython on ESP8266 (十八) : SSD1306 液晶顯示器測試

含 SSD1306 的程式如下 :

#myapp.py
import time
from machine import UART
import ssd1306
from machine import Pin, I2C

i2c=I2C(scl=Pin(5), sda=Pin(4))              
oled=ssd1306.SSD1306_I2C(128, 32, i2c)

def LED_blink(pin,s):
    for i in range(0,s):
        pin.value(1)
        time.sleep_ms(500)
        pin.value(0)
        time.sleep_ms(500)

LEDPIN=Pin(2, Pin.OUT)

uart=UART(0,9600)
uart.init(9600, bits=8, parity=None, stop=1)

oled.fill(0)
oled.text('waiting for Y', 0, 0)
oled.show()

uart.write('Y')

while True:
    if uart.any():
        val=uart.read()
        oled.text(val, 0, 0)  
        oled.show()  
        if val.find(b"Y") != -1:
            oled.fill(0)  
            oled.text('Got Y', 0, 8)
            oled.show()  
            LED_blink(LEDPIN,2)
            uart.write('Y')          
        oled.fill(0)  
        oled.text('waiting for Y', 0, 0)  
        oled.show()      
    time.sleep_ms(1)

注意, 上面這個 myapp.py 可以搭配下列文章末尾所附的 AP 設定程式 main.py 中的 1 或 3 (建議用 3, 這個會顯示 WiFi 連線狀態) :

# # WeMOS D1 Mini 開發板測試




當 REPL 不能使用, ESP8266 變成一個黑盒子時, SSD1306 顯示器就變成唯一的內視鏡了, 哈哈哈. 注意, 這裡兩方互傳的是單一字元 'Y', 其實也可以傳例如 'Hello World!' 的字串.

上面測試 2 只是個遊戲, 目的是驗證 MicroPython on ESP8266 的 UART 功能可正常運作. 接下來要做個具有實用性質的測試, 我在 Arduino Nano 的 A0 腳接一個光敏電阻 + 10K 歐姆分壓電路量測亮度, 透過 UART 將測得的亮度數據傳送給 ESP8266, 並顯示於 SSD1306 OLED 顯示器上, 參考之前在 Arduino 所做的測試 :

Arduino 光敏電阻測試

不同之處在於此處我將光敏電阻與 10K 位置顛倒, 光敏電阻上接 5V 電源, 10K 電阻則是接地, 這樣亮度就會與量測到的數據成正比, 因亮度強時光敏電阻值降低, 10K 歐姆壓降增加, 讀取到的數值也較大. 電路圖如下 :




測試 3 : 利用 UART 將 Arduino 從光敏電阻測量之亮度數據傳給 ESP8266

Arduino 程式 :

#include <SoftwareSerial.h>
SoftwareSerial sSerial(11,10);
int value;
int LED=13;

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(9600);      
  sSerial.begin(9600);    
  }

void loop() {
  value=analogRead(A0);
  int percent=map(value,0,1023,0,100);
  Serial.print("Luminance=");
  Serial.print(percent);
  Serial.println("%");
  sSerial.print((String)percent + "%");
  delay(1000);
  }

此程式每 1 秒測量亮度一次, 利用 map() 函數將 ADC 轉換取得之 0~1023 亮度轉成 1~100 的百分數, 後面串上 '%' 後透過軟體串列埠傳送給 ESP8266. 注意, 此處為了 ESP8266 端數據處理方便, 改用 Serial.print() 而非 Serial.write() 來傳送, 因為 print() 是以可讀之 ASCII 碼傳送, 而 write() 則是使用 Bytes.

MicroPython 程式 :

#myapp.py
import time
from machine import UART
import ssd1306
from machine import Pin, I2C

i2c=I2C(scl=Pin(5), sda=Pin(4))            
oled=ssd1306.SSD1306_I2C(128, 32, i2c)

def LED_blink(pin,s):
    for i in range(0,s):
        pin.value(1)
        time.sleep_ms(500)
        pin.value(0)
        time.sleep_ms(500)

LEDPIN=Pin(2, Pin.OUT)

uart=UART(0,9600)
uart.init(9600, bits=8, parity=None, stop=1)

while True:
    if uart.any():
        val=uart.read()
        line1=str(val).replace("b","").replace("'","")
        oled.text(line1, 0, 0)
        oled.show()          
        oled.fill(0)    
    time.sleep_ms(500)

此程式在無限迴圈中以 0.5 秒週期讀取 UART(0) 串列埠, 讀取資料的速度一定要比傳送端 Arduino 送出數據的速度快才行, 這樣才能確保每次只讀到一筆資料, 否則就可能一次讀到兩筆資料. 而 uart.read() 會讀取並清除 RX 緩衝區內收到的資料, 傳回例如 b'12%' 的 Bytes 類型資料, 利用 str() 予以強制轉型為字串後便可利用字串物件的 replace() 方法擷取出其中的數據 '12%', 此處使用了兩次 replace() 來清除收到資料中的 'b' 與 "'" 單引號, 然後將其顯示在 SSD1306 OLED 顯示器上.



雖然 SSD1306 字較小, 但還是可看出其數值每秒會更新一次 (全螢幕觀看較清楚).

SSD1306 可以顯示 4 列文字 (4*16=64 個字), 而上面的範例只能一次顯示一個數據在第一列, 可否將收到的數據顯示在第四列, 其他舊數據往上推呢? 作法如下測試 4, 只需要修改 MicroPython 程式 :


測試 4 : 利用 UART 將 Arduino 從光敏電阻測量之亮度數據傳給 ESP8266 (顯示最近 4 筆)

#myapp.py
import time
from machine import UART
import ssd1306
from machine import Pin, I2C

i2c=I2C(scl=Pin(5), sda=Pin(4))          
oled=ssd1306.SSD1306_I2C(128, 32, i2c)

def LED_blink(pin,s):
    for i in range(0,s):
        pin.value(1)
        time.sleep_ms(500)
        pin.value(0)
        time.sleep_ms(500)

LEDPIN=Pin(2, Pin.OUT)

uart=UART(0,9600)
uart.init(9600, bits=8, parity=None, stop=1)
lines=["","","",""]

while True:
    if uart.any():
        val=uart.read()
        lines[0]=lines[1]  
        lines[1]=lines[2]
        lines[2]=lines[3]
        lines[3]=str(val).replace("b","").replace("'","")
        oled.text(lines[0], 0, 0)    
        oled.text(lines[1], 0, 8)    
        oled.text(lines[2], 0, 16)
        oled.text(lines[3], 0, 24)
        oled.show()        
        oled.fill(0)  
    time.sleep_ms(500)

這樣就能一次看到最近四筆資料了.




如果是使用 LCD1602 顯示器, 則程式要修改為如下測試 5 :


測試 5 : 利用 UART 將 Arduino 從光敏電阻測量之亮度數據傳給 ESP8266 (使用 LCD1602)


#myapp.py
import time
from machine import UART
from machine import Pin, I2C
from esp8266_i2c_lcd import I2cLcd  

i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)  
lcd=I2cLcd(i2c, 0x27, 2, 16)

uart=UART(0,9600)
uart.init(9600, bits=8, parity=None, stop=1)

while True:
    if uart.any():
        val=uart.read()
        line1=str(val).replace("b","").replace("'","")
        lcd.clear()  
        lcd.putstr(line1)  
    time.sleep_ms(500)  




注意, 與 SSD1306 不同之處是, LCD1602 需在顯示文字之前先呼叫 clear() 清除螢幕. 關於 LCD1602 用法參考 :

MicroPython on ESP8266 (十七) : 液晶顯示器 1602A 測試

如果要使用兩列來顯示最近兩筆資料, 程式如下 :

#myapp.py
import time
from machine import UART
from machine import Pin, I2C
from esp8266_i2c_lcd import I2cLcd

i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)  
lcd=I2cLcd(i2c, 0x27, 2, 16)

uart=UART(0,9600)
uart.init(9600, bits=8, parity=None, stop=1)
lines=["",""]

while True:
    if uart.any():
        val=uart.read()
        lines[0]=lines[1]
        lines[1]=str(val).replace("b","").replace("'","")
        lcd.clear()
        lcd.move_to(0, 0)
        lcd.putstr(lines[0])
        lcd.move_to(0, 1)
        lcd.putstr(lines[1])
    time.sleep_ms(500)

不過因為 LCD1602 只有兩列, 所以感覺不出數據往上移動, 反而像是同時顯示兩筆資料一樣, 容易讓人搞不清楚哪一筆才是最新的資料.

OK, UART 測試大功告成, 以上的實驗最重要的啟示是, 雖然 ESP8266 模組的 ADC 埠不像 Arduino 的 A0~A7 這麼多, 但是透過 UART 可以讓 ESP8266 利用 Arduino 的數位或類比埠來擴充功能, 或許在機器人或智慧小車應用上可以採用 ESP8266 + Arduino 雙模組設計, ESP8266 負責 WiFi 連線與較複雜計算; 而 Arduino 負責控制感測器並將測量數據透過 UART 傳給 ESP8266 進行運算, 如果覺得 9600 bps 的鮑率太低的話, 也可以將 Arduino 軟體串列埠與 ESP8266 的速率都提高為 115200 bps.

總結以上 UART 測試, 歸納為下列兩點 :
  1. 程式使用 UART(0) 會讓 ampy/mpfshell 無法使用, 必須透過 WebREPL 上傳程式檔
  2. 因為無法使用 REPL, 因此最好使用 LCD/OLED 顯示執行狀態

參考 :

HOW TO USE UART ?  (Lua NodeMCU, ESP-12 的 UART2 是 GPIO13 與 GPIO15?)
ESP8266 D1 mini傳送資料到MQTT Server、接收資料進行相關控制
working with UART and Bytearrays
What is the exact syntax to use UART pin of nodemcu using micropython?

19 則留言:

  1. 您好,请教一下,当我使用webREPL 输入 uart=UART(0,9600) 的时候,页面就开始大量的输出一堆日志,您没有遇到这个问题吗?

    回覆刪除
  2. 耗子兄您好, 我沒遇過這種情形, 但 UART 0 是板上 REPL 所用的 TX/RX, 因此若前面有執行列印指令, 可能看到那些輸出, 但印出日志就奇怪了.

    回覆刪除
  3. 非常荣幸能收到宝岛台湾的朋友的回信,我是做java开发的,对单片机这块纯粹是出于自己的兴趣爱好,还有好多疑惑的地方希望能像您请教,不知道是否方便加一下您的twitter,我的twitter 是 CrazyMouse_TT,非常感谢

    回覆刪除
  4. 謝謝您留言, 我也是從 Java 開始學編程的, 但從沒寫過甚麼專案. 因為使用 Google App Engine 接觸 Python 後就把 Java 給扔了, 專心學習 Python. 樂意加推特, 因少用不熟悉, 要琢磨一下.

    回覆刪除
  5. 或者说你们用哪个工具用得多一些,我也可以用它来和你联系的,脸书?还是whatsapp?

    回覆刪除
  6. 台灣大部分人用 Line, 您有 Line ID 嗎? 我可以加你.

    回覆刪除
  7. 經研究後, 我知道大概知道如何用了, 我已隨您推特囉! 我推特大都拿來做實驗.

    回覆刪除
  8. 大陆这边几乎就是微信的天下了,唉,我看到你的Twitter了

    回覆刪除
  9. 您是66年的啊,都是老大哥了

    回覆刪除
  10. 大陆这边封锁了line,vpn连接line也不好使,倒是twitter用vpn还能使用,我现在看您的blog也需要vpn,悲催的大陆

    回覆刪除
  11. 了解, 我幾個同學也在大陸工作, 不能用 line 也很麻煩. 我們這裡用甚麼都可以, 但 line 最受歡迎, 臉書漸漸被取代. 以前我也安裝過 whatsapp (中文叫微信嗎?), 但介面不習慣, 換手機後就沒再裝了. 哈, 你們經歷的, 我們 60 年代的人都走過, 感覺像是複習歷史課一樣. 長夜漫漫總有天明的時候. 抓緊時間多學習才是要緊的事.

    回覆刪除
  12. 不知道是否能等到见到光明的那一天,唉,大陆再出一个‘蒋经国’基本上是不可能的了,民族道路漫长。真的很羡慕你们,虽然说地方不大,但空气好,食品安全。北京今天PM2.5我在家里测试的已经125了。whatsapp 不是微信,微信叫weichat

    回覆刪除
  13. 雖然空氣較好, 食品較安全, 但不時還是會爆出黑心商品. 所幸媒體常爆料, 社會監督政府加強取締, 一個頂新案就把無良魏家趕出台灣, 損失慘重. 台灣整天媒體與政客吵吵鬧鬧, 立法院經常演出全武行, 官員, 名人或藝人上摩鐵, 第二天就見報, 政論節目天天罵來罵去, 我們每天都不愁沒話題. 我幾年前因公去過上海, 驚覺比台北還進步, 學子認真學習程度超越我們甚多. 我表弟在北京 Benz 工作了兩年也是對空氣敏感, 今年調德國去了. 我們二十幾年前經濟大成長時也是汙染嚴重, 但環保意識抬頭後就改善了. 物種在進化, 社會也是會進化的.

    回覆刪除
  14. 可是你们有选票,看得到希望,我们几乎是看不到未来,中产阶级都是人人自危的感觉,并且大陆现在网络审查越来越严格了

    回覆刪除
  15. 看到您的blog是在1年多前寫的:) 我正在使用esp8266,也嘗試過使用uart0 連接一個ttl 設備進行讀寫,發現和repl 會沖突後換了esp32。謝謝你寫的詳細的測試過程,我想重新試試esp8266.

    回覆刪除
  16. Hi, ESP32 功能比 ESP8266 強, 但價位也較高. 小型物聯網應用 ESP8266 即綽綽有餘, ESP32 反而大材小用.

    回覆刪除
  17. 是的,通過8266讀取數據然後通過WIFI傳輸出去,我覺得WIFI 互聯可產生很多玩法。
    前幾天做了一個WIFI 轉RS232的功能, 後來發現淘寶網路上已經有現成產品賣,還很便宜~

    回覆刪除
  18. 的確是這樣。相當於把一個看似沒有邊界的世界界定出一個”邊界“,哈哈。
    因為可以加入特殊協議的轉換和處理,所以會繼續做完.包含web server, uart, ftp.

    回覆刪除