(2022/08/03)
前陣子一樣從宇宙機器人取得Mason所設計的【Gyro擴展板】,這板子支援的開發板有NodeMCU-32S及ESP32-CAM,由於我已經利用其他擴展板玩過NodeMCU-32S,再加上市面上比較少見ESP32-CAM的擴展板,因此這次聚焦在ESP32-CAM的使用!這次測試的另一個重點是利用MicroPython來寫程式,也藉此來學習MicroPython,由於Gyro擴展板的官網資料非常充實,最後成功的完成利用手機APP控制ESP32-CAM小車,入門資料如下,供參
一、ESP32-CAM與Gyro擴展板的結合
認識ESP32-CAM
腳位圖
GPIO0 :燒錄用
GPIO1:U0TX
GPIO3:U0RX
GPIO4:閃光燈共用
剛好看到Gyro擴展板有支援ESP32-CAM,所以就用兩者的結合來學習MicroPython
認識Gyro擴展板
Gyro擴展板官網:
支援兩款 ESP32 開發板
ESP32 nodemcu-32s
ESP32CAM
官網的資料非常多,如這次想學的MicroPython
一、安裝Thonny
Thonny官網:https://thonny.org/
下載3.3.13版:
https://github.com/thonny/thonny/releases/download/v3.3.13/thonny-3.3.13.exe
直接下載安裝版,也有ZIP檔
也有4.0.03b版
二、下載ESP32的韌體
下載 v1.19.1 (2022-06-18) .bin (目前最新的版本) https://micropython.org/resources/firmware/esp32-20220618-v1.19.1.bin
三、開啟Thonny及燒錄ESP32的韌體到ESP32-CAM
將電腦與ESP32-CAM+Gyro擴展板連接(透過USB TO TTL,如下圖),韌體燒錄時,記得要將IO0與GND連接(如下圖的【黃線】)(或是按下方中間偏右方的按鍵(IO0)也可以,我沒試過,這是後來才發現的)
開啟Thonny
點擊 工具/選項
點擊【直譯器】
選擇【MicroPython(ESP32)】
選擇正確的【連接埠】
點擊【安裝或更新韌體】
以下幾塊開發版均可用同樣方法來燒錄韌體!
NodeMCU-32S
Web:Bit
PocketCard
測試一:按鍵點燈
LED燈一閃一滅 (1秒)
說明:ESP32-CAM有內建閃光燈,其腳位為GPIO4,亮度很強
MicroPython程式:
from machine import Pin #使用外部模組功能的動作叫做匯入 (import),匯入的有類別或函式,這邊是匯入Pin類別
import time #from time import sleep ,後面sleep(1)
led = Pin(4 , Pin.OUT) #建立Pin物件,名稱叫led ,內建閃光燈
while True:
led.value(1) # 也可使用led.on()
time.sleep(1) #等待1秒,也可用 time.sleep_ms(1000)
led.value(0) # 也可使用led.off()
time.sleep(1)
按擴展板的按鍵,點亮內建的閃光燈
擴展板按鍵腳位說明:
SW1:GPIO0 (位於下方中間偏右)
SW2:GPIO12(位於左下方),要使用上拉電阻
SW3:ESP32-CAM不能用(位於右下方)
SW4:GPIO13 (位於下方中間偏左),要使用上拉電阻
MicroPython程式:
from machine import Pin
led = Pin(4, Pin.OUT)
button = Pin(0, Pin.IN) #SW1
led.on() #led.value(1) 也可以
while True:
if button.value() == 0:
led.on()
else:
led.off()
按右鍵(SW1)點燈、按左鍵(SW4)關燈
MicroPython程式:
from machine import Pin
led = Pin(4, Pin.OUT)
buttonA = Pin(0, Pin.IN) #右鍵(SW1)
buttonB = Pin(13, Pin.IN, Pin.PULL_UP) #左鍵 (SW4)
while True:
if buttonA.value() == 0:
led.on()
elif buttonB.value() == 0:
led.off()
如果按鍵不是採用【模組】,而是只有2支腳(一支接GND)時,設定【上拉電阻】如下:button = Pin(13, Pin.IN, Pin.PULL_UP)
將檔案儲存在【MicroPython設備】(ESP32上),並將檔名存成main.py,就可離線執行
測試二:OLED顯示
下載ssd1306 OLED驅動模組
網址:https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py
複製程式碼到Thonny下,並另存到ESP32的設備上 (如下圖說明)
在LED上顯示Hello World
簡單程式如下:
from machine import Pin, SoftI2C #匯入Pin及I2C模組
import ssd1306 #匯入ssd1306模組
i2c = SoftI2C( scl=Pin(1), sda=Pin(3), freq=400000) #建立SoftI2C類別物件
width = 128
height = 64
display = ssd1306.SSD1306_I2C(width, height,i2c) #建OLED物件
display.text('Hello World',0 ,0) #ssd1306函式
display.show() #顯示螢幕
Gyro擴展板官網提供了很多常用元件的外部模組(module)供下載
下載網址:https://drive.google.com/drive/folders/1vcqCDC7hxVru0UnvaESC7T9IQsltu_Fw
下載module.zip
解壓如下:
要使用這些模組前都要先把這些檔案複製(或存檔)到MicroPython設備上去!!!
前個範例OLED顯示模組為ssd1306.py,這邊也可以採用OLED.py這個模組
以下程式取自Gyro官網
import OLED
oled = OLED.SSD1306_I2C(128, 64)
oled.rotate_s(0)
oled.text('Hello!', 0, 0)
oled.show()
使用OLED.py模組前記得要把裡面I2C腳位改成SP32-CAM的腳位
要修改在第112行
測試三:蜂鳴器測試
使蜂鳴器發出 1KHz 的 Bee Bee 告警聲
腳位說明:
蜂鳴器:GPIO2
這邊只能用【兩支腳】的蜂鳴器,不能用【三支腳】的蜂鳴器模組
MicroPython程式:
from machine import Pin,PWM
import time
def alarmBeep(pwm):
pwm.init() #初始化PWM ,之前沒設這條出錯誤
pwm.freq(1000) #設定頻率為 1KHz
pwm.duty(512) #設定工作週期為 50%
time.sleep(1) #持續時間 1 秒
pwm.deinit() #停止發聲
time.sleep(2) #持續時間 2 秒
pwm=PWM(Pin(2))
while True:
alarmBeep(pwm)
一開始一直出現"ValueError: failed to bind timer to channel"錯誤,在函式一開始加個pwm.init()就解決了!
按SW1鍵播放全家歡迎的音樂
全家歡迎音樂簡譜:3 1 5(低音) 1 / 2 5~ 2 / 3 2 5(低音) 1
MicroPython程式:
from machine import Pin,PWM
import time
buttonA = Pin(0, Pin.IN) #SW1鍵
buzzer = 2 #蜂鳴器
pwm=PWM(Pin(buzzer))
pwm.deinit() #去除一開始的雜音
def family(pwm):
pwm.init() #初始化PWM
pwm.duty(512)
pwm.freq(330)
time.sleep(0.5)
pwm.freq(262)
time.sleep(0.5)
pwm.freq(196)
time.sleep(0.5)
pwm.freq(262)
time.sleep(0.5)
pwm.freq(294)
time.sleep(0.5)
pwm.freq(392)
time.sleep(1)
pwm.freq(294)
time.sleep(0.5)
pwm.freq(330)
time.sleep(0.5)
pwm.freq(294)
time.sleep(0.5)
pwm.freq(196)
time.sleep(0.5)
pwm.freq(262)
time.sleep(0.5)
pwm.deinit()
while True:
if buttonA.value() == 0:
family(pwm)
測試四:超音波測距感測器測試
顯示HC-SR04P所量測到的距離
腳位:
TRIG:GPIO0
ECHO:GPIO2
MicroPython程式:
import Ultrasonic
import utime
while True:
distance = Ultrasonic.read(0, 2)
print("偵測距離: " + str(distance) + " 公分")
utime.sleep_ms(250)
Ultrasonic.py:(取自Gyro官網)
from machine import Pin
from machine import time_pulse_us
from time import sleep_us
def read(trig_pin, echo_pin):
trigger = Pin(trig_pin, mode=Pin.OUT)
echo = Pin(echo_pin, mode=Pin.IN)
trigger.value(0)
sleep_us(5)
trigger.value(1)
sleep_us(10)
trigger.value(0)
try:
pulse_time = time_pulse_us(echo, 1, 1000000)
d = (pulse_time / 2) / 29.1
return int(d) if d < 400 else -1
except OSError as ex:
return -1
Mason老師解釋:會有亂碼應該是正常的,因為 esp32cam 的腳位很有限。 所以當初腳位規劃試了很久,最後我把原來的 TX / RX 拿來當 i2c 用,可以換到更好的效益. 因此, 用 i2c 時 uart 可能就會出現亂碼了.
測試五:L9110S
讓車子前後左右走3秒鐘
腳位說明:
m1 = L9110S(14, 15)
m2 = L9110S(13, 12)
A-1B(12)、A-1A(13)、B-1B(15)、B-1A(14)
car.move(1):前進
car.move(2):後退
car.move(3):原地左轉
car.move(4):原地右轉
car.stop():停止
MicroPython程式:
from motor import L9110S,Car
from time import sleep
m1 = L9110S(14, 15)
m2 = L9110S(13, 12)
car=Car(m1,m2); car.wheel(1, 1)
car.move(1) #測試時發現,這段(第一段)程式沒有反應,真奇怪,如果刪掉此,換下一段的car.move(1)沒作
sleep(0.5)
car.move(1)
sleep(3)
car.stop()
sleep(1)
car.move(2)
sleep(3)
car.stop()
sleep(1)
car.move(3)
sleep(3)
car.stop()
sleep(1)
car.move(4)
sleep(3)
car.stop()
由於 ESP32CAM 耗電量很大,請使用【外部電源供電】,且務必將右上角的 "jumper" 移除,讓 5V 供電由外部電源供應,避免電流不足造成當機或其它不穩等現象。否則Thonny互動環境下會出現一大堆字且>>>會出不來!
按SW1鍵車子向前、按外接按鍵車子向後
三支腳的外接按鍵模組接在GPIO2
MicroPython程式:
from machine import Pin
from motor import L9110S,Car
from time import sleep
m1 = L9110S(14, 15)
m2 = L9110S(13, 12)
car=Car(m1,m2); car.wheel(1, 1)
buttonA = Pin(0, Pin.IN) #右鍵(SW1)
buttonB = Pin(2, Pin.IN) #三腳的按鍵模組
while True:
if buttonA.value() == 0: #按SW1鍵車子向前
car.move(1)
sleep(1)
car.stop()
elif buttonB.value() == 1: #按外接按鍵車子向後
car.move(2)
sleep(1)
car.stop()
測試六:藍牙控制
用手機 BLE Scanner app 透過 BLE 來控制閃光燈
手機APP:BLE Scanner(如右圖)
參考資料:以下內容完全取自Gyro擴展板的官網
所需程式模組:ble_uart、tools、const(先另存到MicroPython設備上)
MicroPython程式:完全抄自官網
import ubluetooth as bt
from ble_uart import BLEUART
from tools import BLETools
from const import BLEConst
from machine import Pin
def rx_callback(data):
print((data))
if (data) == (b'on'):
Pin(4, Pin.OUT).value(1) #改用閃光燈的腳位4
if (data) == (b'off'):
Pin(4, Pin.OUT).value(0) #改用閃光燈的腳位4
ble = bt.BLE(); uart = BLEUART(ble, rx_callback,name="esp32cam_ble") #顯示名稱
手機端的操作
Thonny所呈現的畫面
用手機 BLE Scanner app 透過 BLE 來控制小車
MicroPython程式:完全抄自官網
import ubluetooth as bt
from ble_uart import BLEUART
from tools import BLETools
from const import BLEConst
from machine import Pin
from motor import L9110S,Car
m1 = L9110S(14, 15)
m2 = L9110S(13, 12)
car=Car(m1,m2); car.wheel(1, 1)
def rx_callback(data):
print((data))
if (data) == (b'f'):
car.move(1) #前進
if (data) == (b'b'):
car.move(2) #後退
if (data) == (b'l'):
car.move(3) #左轉
if (data) == (b'r'):
car.move(4) #右轉
if (data) == (b's'):
car.stop() #停止
ble = bt.BLE(); uart = BLEUART(ble, rx_callback,name="esp32cam_car_ble")
測試成功
用手機V7RC app 透過 BLE 來控制小車
MicroPython程式:完全抄自官網
from motor import L9110S,Car
import ubluetooth as bt
from ble_uart import BLEUART
from tools import BLETools
from const import BLEConst
import math
CH1 = None
CH2 = None
def rx_callback(data):
CH1 = int(((data).decode('utf-8')[3:7]),10)
CH2 = int(((data).decode('utf-8')[7:11]),10)
print(CH1)
if math.fabs(CH1 - 1500) < 100 and math.fabs(CH2 - 1500) < 100:
car.stop()
elif math.fabs(CH1 - 1500) > math.fabs(CH2 - 1500):
if CH1 > 1500:
car.move(4) #右轉
else:
car.move(3) #左轉
else:
if CH2 > 1500:
car.move(1) #前進
else:
car.move(2) #後退
CH1 = 1500
CH2 = 1500
m1 = L9110S(14, 15)
m2 = L9110S(13, 12)
car=Car(m1,m2); car.wheel(1, 1)
ble = bt.BLE(); uart = BLEUART(ble, rx_callback,name="ESP32-CAM_Car")
手機端的操作
Mason老師建議用【坦克】會比較好:因為【坦克】模式可抓四個 channel 的資料來. 【車輛】模式要看 V7RC 實際送的資料是什麼了。 也許剛好可以用.