連接ESP8266控制板和電腦的方式有兩種:
- Wi-Fi無線網路:把ESP8266當作感測器網路前端設備,傳送資料到網站伺服器;或者當作網站伺服器,提供API讓前端網頁取得感測數據。
- 序列通訊埠:透過USB序列埠跟電腦或者手機連線。
使用Wi-Fi連線顯然比較符合ESP8266的天性,但是對於沒有內建Wi-Fi的MicroPython控制板或者Arduino板,USB是最普遍的選擇。本文將介紹使用Python程式與Arduino和MicroPython(ESP8266板)進行序列通訊的方法。
使用pySerial進行序列通訊
Python的序列埠通訊套件叫做pySerial,請在終端機透過pip進行安裝:
pySerial提供初始化序列埠、傳送和接收序列數據的指令,像read(), readline()和write()基本指令名稱和語法,跟MicroPython的UART模組一樣。一開始,我們先從Arduino傳送序列資料給Python接收,來認識pySerial模組的用法。
底下是每隔一秒傳送“hello!”訊息的Arduino程式:
unsigned long previousMillis = 0; const long interval = 1000; // 設定間隔時間,1000ms char chr; void setup() { Serial.begin(9600); } void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; Serial.println("hello!"); } }
底下是採用pySerial模組接收序列資料的Python程式碼,它將不停地接收與顯示收到的序列資料,直到你按下Ctrl和C鍵為止。筆者假設Arduino所在的序列埠名稱是COM6,請自行修改。macOS系統的通訊埠名稱類似這樣:/dev/tty.usbmodem231。
import serial # 引用pySerial模組 COM_PORT = 'COM6' # 指定通訊埠名稱 BAUD_RATES = 9600 # 設定傳輸速率 ser = serial.Serial(COM_PORT, BAUD_RATES) # 初始化序列通訊埠 try: while True: while ser.in_waiting: # 若收到序列資料… data_raw = ser.readline() # 讀取一行 data = data_raw.decode() # 用預設的UTF-8解碼 print('接收到的原始資料:', data_raw) print('接收到的資料:', data) except KeyboardInterrupt: ser.close() # 清除序列通訊物件 print('再見!')
連接Arduino,再執行上面的Python程式,將能收到來自Arduino的訊息。原始資料是byte格式:
使用Python序列程式控制Arduino或MicroPython板的LED
本單元的Python程式將在終端機顯示:「按1開燈、按2關燈、按e關閉程式」;若用戶按下1,Python將透過序列埠傳遞代表開燈的b’LED_ON\n’訊息;若按下2,則送出代表開燈的b’LED_OFF\n’訊息,這兩個訊息都是筆者自訂的,訊息內容必須是byte格式。
電腦端的Python序列通訊程式碼如下:
import serial from time import sleep import sys COM_PORT = 'COM8' # 請自行修改序列埠名稱 BAUD_RATES = 9600 ser = serial.Serial(COM_PORT, BAUD_RATES) try: while True: # 接收用戶的輸入值並轉成小寫 choice = input('按1開燈、按2關燈、按e關閉程式 ').lower() if choice == '1': print('傳送開燈指令') ser.write(b'LED_ON\n') # 訊息必須是位元組類型 sleep(0.5) # 暫停0.5秒,再執行底下接收回應訊息的迴圈 elif choice == '2': print('傳送關燈指令') ser.write(b'LED_OFF\n') sleep(0.5) elif choice == 'e': ser.close() print('再見!') sys.exit() else: print('指令錯誤…') while ser.in_waiting: mcu_feedback = ser.readline().decode() # 接收回應訊息並解碼 print('控制板回應:', mcu_feedback) except KeyboardInterrupt: ser.close() print('再見!')
使用Arduino Uno進行序列通訊
接收來自電腦Python的序列訊息,開、關第13腳的LED的Arduino程式碼如下:
#define LED 13 String str; void setup() { pinMode(LED, OUTPUT); Serial.begin(9600); } void loop() { if (Serial.available()) { // 讀取傳入的字串直到"\n"結尾 str = Serial.readStringUntil('\n'); if (str == "LED_ON") { // 若字串值是 "LED_ON" 開燈 digitalWrite(LED, HIGH); // 開燈 Serial.println("LED is ON"); // 回應訊息給電腦 } else if (str == "LED_OFF") { digitalWrite(LED, LOW); Serial.println("LED is OFF"); } } }
使用MicroPython(Wemos D1 mini板)進行序列通訊
底下是在ESP8266控制板(D1 mini)執行microPython,接收序列訊息的程式:
from machine import Pin from machine import UART com = UART(0, 9600) com.init(9600) led = Pin(2, Pin.OUT, value=1) print('MicroPython Ready...') while True: choice = com.readline() if choice == b'LED_ON\n': led.value(0) com.write(b'LED is ON!\n') # 回應訊息給電腦端的Python elif choice == b'LED_OFF\n': led.value(1) com.write(b'LED is OFF!\n')
筆者將此程式命名成ser_test.py檔,再使用ampy工具上傳到D1 mini板。上傳完畢後,請先按一下D1 mini板的Reset(重置)鍵。
接著使用WebREPL連線到D1 mini板,透過import ser_test執行序列通訊測試程式,相關操作說明請參閱《超圖解 Python 物聯網實作入門》第七章「序列埠通信」7-21頁。
執行此Python序列通訊程式的操作畫面如下:
請問一下,我在這個程式中加入split()
希望能夠控制碼能夠更加多元
但是他都沒反應,但我用putty進入到micropython中測試split()這個函式
可是卻是可以用的,不知道哪邊有錯呢?
a.py
import serial
from time import sleep
import sys
COM_PORT = ‘COM3’ # 請自行修改序列埠名稱
BAUD_RATES = 9600
ser = serial.Serial(COM_PORT, BAUD_RATES)
try:
while True:
# 接收用戶的輸入值並轉成小寫
choice = input(‘按1開燈、按2關燈、按e關閉程式 ‘).lower()
if choice == ‘1’:
print(‘傳送開燈指令’)
ser.write(b’LED_ON,2\n’) # 訊息必須是位元組類型
sleep(2) # 暫停0.5秒,再執行底下接收回應訊息的迴圈
elif choice == ‘2’:
print(‘傳送關燈指令’)
ser.write(b’LED_OFF,1\n’)
sleep(2)
elif choice == ‘e’:
ser.close()
print(‘再見!’)
sys.exit()
else:
print(‘指令錯誤…’)
except KeyboardInterrupt:
ser.close()
print(‘再見!’)
main1.py
from machine import Pin
from machine import UART
com = UART(0, 9600)
com.init(9600)
led = Pin(2, Pin.OUT, value=1)
print(‘MicroPython Ready…’)
while True:
choice = com.readline()
choice1=choice.split(b’,’)
if choice1[0] == b’LED_ON’:
led.value(0)
elif choice1[0] == b’LED_OFF’:
led.value(1)
可以,5-26有split()的語法示範,7-25頁的GPS資料解析也有用到split()。應該是你忘記考量’\n’字元,請嘗試解碼後再分割:
choice, num = com.readline().split(b’\n’)[0].decode().split(‘,’)
thanks,
jeffrey
我有考量到\n
因為他在後面
所以 並不影響choice1[0]
同樣的方式
a=b’LED_ON,22,3\n’
b=a.split(b’,’)
if b[0]==b’LED_ON’:
print(’12’)
在putty是可行的
但是 如果用在老師提供的方法執行 卻是不可行
甚至是加入decode我都試過 但都不行
所以我在想 micropython可能有bug
我剛剛實機測試了一下,發現是因為缺少判斷傳入值為None的情況:
thanks,
jeffrey
老師您好!請問一下ser.write(b’LED_ON\n’) 中的LED_ON\n,我想要改成變數形式改如何做?謝謝老師
想請問在read,write之間有什麼常見的問題嗎?
我在micropython on esp32做了以下測試後,渴望您的見解
1.板子:com.write(b”xxx”) -> 電腦:ser.readline().decode()
這是成功的
但反向時
2.電腦:ser.write(b”xxx”) ->板子:com.readline()
收不到資料,都是None
但是,不用readline()接收,空的main,單純開webrepl,在>>>列上是有收到ser.write()過來的資料的…
感激不盡
我最慢明天更新一篇文章說明。
文章已發布,請參閱「ESP32 MicroPython與Python的pySerial模組進行UART序列通訊」。
thanks,
jeffrey