2023年3月14日 星期二

樹莓派學習筆記 : GPIO 測試 (二) gpiozero 模組

在前一篇 GPIO 測試中使用了樹莓派內建的 RPi.GPIO 模組來存取 GPIO 腳位, 此模組較為低階, 所有的 GPIO 腳須自行定義其用途. 樹莓派其實還內建了另一個較為高階的 gpiozero 模組, 它為 GPIO 的常用輸出入用途定義了可方便呼叫的函式, 使得程式碼長度相對較簡短. 

本系列之前的文章參考 :


為了測試時接線方便, 我將 GPIO 的 40 支腳位置圖重貼如下 :




3. 使用 gpiozero 模組 : 

前一篇測試中使用的 RPi.GPIO 模組只能指定任一腳位是輸出或輸入, 至於該腳位接哪種元件與做何用途完全由使用者去定義, 好處是靈活度大, 但缺點是程式碼較繁瑣; 而 gpiozero 則不同, 此模組替各種元件定義了相對應之類別, 例如處理 LED 輸出有 LED 類別, 處理蜂鳴器輸出有 Buzzer 類別等等, gpiozero 可以說是一個特化的模組, 它可使程式碼更簡短, 教學文件參考 :


樹莓派 OS 的 Python3 已經搭載 gpiozero 模組不需安裝, 直接 import 使用即可. 但若使用 Lite 版的 OS 則可用下列指令安裝 :

sudo apt update   
sudo apt install python3-gpiozero   

如果樹莓派安裝的別的作業系統, 則可用 pip instal gpiozero 來安裝. 我的 Pi A+ 是比較舊的 Rasbian Buster, 未搭載 gpiozero, 所以需另行安裝 :

pi@raspberrypi:~ $ sudo apt install python3-gpiozero    
正在讀取套件清單... 完成
正在重建相依關係          
正在讀取狀態資料... 完成
建議套件:
  python-gpiozero-docs
下列套件將會被升級:
  python3-gpiozero
升級 1 個,新安裝 0 個,移除 0 個,有 504 個未被升級。
需要下載 121 kB 的套件檔。
此操作完成之後,會多佔用 97.3 kB 的磁碟空間。
下載:1 http://archive.raspberrypi.org/debian buster/main armhf python3-gpiozero all 1.6.2-1 [121 kB]
取得 121 kB 用了 2s (62.1 kB/s)            
讀取改變紀錄(changelogs)... 完成
(讀取資料庫 ... 目前共安裝了 153943 個檔案和目錄。)
正在準備解包 .../python3-gpiozero_1.6.2-1_all.deb……
Unpacking python3-gpiozero (1.6.2-1) over (1.5.1) ...
設定 python3-gpiozero (1.6.2-1) ...
執行 man-db (2.8.5-2) 的觸發程式……

接下來用自訂模組 members 的 list_members() 函式來檢視 gpiozero 的成員 : 

# members.py
import inspect 
def varname(x): 
    return [k for k,v in inspect.currentframe().f_back.f_locals.items() if v is x][0]
def list_members(parent_obj):
    members=dir(parent_obj)
    parent_obj_name=varname(parent_obj)       
    for mbr in members:
        child_obj=eval(parent_obj_name + '.' + mbr) 
        if not mbr.startswith('_'):
            print(mbr, type(child_obj))

將此 members.py 存放於工作目錄下. 

>>> import gpiozero   
>>> import members   
>>> members.list_members(gpiozero)  
AmbiguousTone <class 'type'>
AnalogInputDevice <class 'gpiozero.devices.GPIOMeta'>
AngularServo <class 'gpiozero.devices.GPIOMeta'>
BadEventHandler <class 'type'>
BadPinFactory <class 'type'>
BadQueueLen <class 'type'>
BadWaitTime <class 'type'>
Button <class 'gpiozero.devices.GPIOMeta'>
ButtonBoard <class 'gpiozero.devices.GPIOMeta'>
Buzzer <class 'gpiozero.devices.GPIOMeta'>
CPUTemperature <class 'gpiozero.devices.GPIOMeta'>
CallbackSetToNone <class 'type'>
CamJamKitRobot <class 'gpiozero.devices.GPIOMeta'>
CompositeDevice <class 'gpiozero.devices.GPIOMeta'>
CompositeDeviceBadDevice <class 'type'>
CompositeDeviceBadName <class 'type'>
CompositeDeviceBadOrder <class 'type'>
CompositeDeviceError <class 'type'>
CompositeOutputDevice <class 'gpiozero.devices.GPIOMeta'>
Device <class 'gpiozero.devices.GPIOMeta'>
DeviceClosed <class 'type'>
DigitalInputDevice <class 'gpiozero.devices.GPIOMeta'>
DigitalOutputDevice <class 'gpiozero.devices.GPIOMeta'>
DiskUsage <class 'gpiozero.devices.GPIOMeta'>
DistanceSensor <class 'gpiozero.devices.GPIOMeta'>
DistanceSensorNoEcho <class 'type'>
Energenie <class 'gpiozero.devices.GPIOMeta'>
EnergenieBadSocket <class 'type'>
EnergenieSocketMissing <class 'type'>
EventsMixin <class 'type'>
Factory <class 'type'>
FishDish <class 'gpiozero.devices.GPIOMeta'>
GPIODevice <class 'gpiozero.devices.GPIOMeta'>
GPIODeviceClosed <class 'type'>
GPIODeviceError <class 'type'>
GPIOPinInUse <class 'type'>
GPIOPinMissing <class 'type'>
GPIOZeroError <class 'type'>
GPIOZeroWarning <class 'type'>
HeaderInfo <class 'type'>
HoldMixin <class 'type'>
InputDevice <class 'gpiozero.devices.GPIOMeta'>
InputDeviceError <class 'type'>
InternalDevice <class 'gpiozero.devices.GPIOMeta'>
JamHat <class 'gpiozero.devices.GPIOMeta'>
LED <class 'gpiozero.devices.GPIOMeta'>
LEDBarGraph <class 'gpiozero.devices.GPIOMeta'>
LEDBoard <class 'gpiozero.devices.GPIOMeta'>
LEDCharDisplay <class 'gpiozero.devices.GPIOMeta'>
LEDCharFont <class 'abc.ABCMeta'>
LEDCollection <class 'gpiozero.devices.GPIOMeta'>
LEDMultiCharDisplay <class 'gpiozero.devices.GPIOMeta'>
LedBorg <class 'gpiozero.devices.GPIOMeta'>
LightSensor <class 'gpiozero.devices.GPIOMeta'>
LineSensor <class 'gpiozero.devices.GPIOMeta'>
LoadAverage <class 'gpiozero.devices.GPIOMeta'>
MCP3001 <class 'gpiozero.devices.GPIOMeta'>
MCP3002 <class 'gpiozero.devices.GPIOMeta'>
MCP3004 <class 'gpiozero.devices.GPIOMeta'>
MCP3008 <class 'gpiozero.devices.GPIOMeta'>
MCP3201 <class 'gpiozero.devices.GPIOMeta'>
MCP3202 <class 'gpiozero.devices.GPIOMeta'>
MCP3204 <class 'gpiozero.devices.GPIOMeta'>
MCP3208 <class 'gpiozero.devices.GPIOMeta'>
MCP3301 <class 'gpiozero.devices.GPIOMeta'>
MCP3302 <class 'gpiozero.devices.GPIOMeta'>
MCP3304 <class 'gpiozero.devices.GPIOMeta'>
MotionSensor <class 'gpiozero.devices.GPIOMeta'>
Motor <class 'gpiozero.devices.GPIOMeta'>
NativePinFactoryFallback <class 'type'>
OutputDevice <class 'gpiozero.devices.GPIOMeta'>
OutputDeviceBadValue <class 'type'>
OutputDeviceError <class 'type'>
PWMLED <class 'gpiozero.devices.GPIOMeta'>
PWMOutputDevice <class 'gpiozero.devices.GPIOMeta'>
PWMSoftwareFallback <class 'type'>
PWMWarning <class 'type'>
PhaseEnableMotor <class 'gpiozero.devices.GPIOMeta'>
PhaseEnableRobot <class 'gpiozero.devices.GPIOMeta'>
PiBoardInfo <class 'type'>
PiHutXmasTree <class 'gpiozero.devices.GPIOMeta'>
PiLiter <class 'gpiozero.devices.GPIOMeta'>
PiLiterBarGraph <class 'gpiozero.devices.GPIOMeta'>
PiStop <class 'gpiozero.devices.GPIOMeta'>
PiTraffic <class 'gpiozero.devices.GPIOMeta'>
Pibrella <class 'gpiozero.devices.GPIOMeta'>
Pin <class 'type'>
PinEdgeDetectUnsupported <class 'type'>
PinError <class 'type'>
PinFactoryFallback <class 'type'>
PinFixedPull <class 'type'>
PinInfo <class 'type'>
PinInvalidBounce <class 'type'>
PinInvalidEdges <class 'type'>
PinInvalidFunction <class 'type'>
PinInvalidPin <class 'type'>
PinInvalidPull <class 'type'>
PinInvalidState <class 'type'>
PinMultiplePins <class 'type'>
PinNoPins <class 'type'>
PinNonPhysical <class 'type'>
PinPWMError <class 'type'>
PinPWMFixedValue <class 'type'>
PinPWMUnsupported <class 'type'>
PinSPIUnsupported <class 'type'>
PinSetInput <class 'type'>
PinUnknownPi <class 'type'>
PinUnsupported <class 'type'>
PinWarning <class 'type'>
PingServer <class 'gpiozero.devices.GPIOMeta'>
PolledInternalDevice <class 'gpiozero.devices.GPIOMeta'>
PololuDRV8835Robot <class 'gpiozero.devices.GPIOMeta'>
PumpkinPi <class 'gpiozero.devices.GPIOMeta'>
RGBLED <class 'gpiozero.devices.GPIOMeta'>
Robot <class 'gpiozero.devices.GPIOMeta'>
RotaryEncoder <class 'gpiozero.devices.GPIOMeta'>
RyanteckRobot <class 'gpiozero.devices.GPIOMeta'>
SPI <class 'gpiozero.devices.GPIOMeta'>
SPIBadArgs <class 'type'>
SPIBadChannel <class 'type'>
SPIDevice <class 'gpiozero.devices.GPIOMeta'>
SPIError <class 'type'>
SPIFixedBitOrder <class 'type'>
SPIFixedClockMode <class 'type'>
SPIFixedRate <class 'type'>
SPIFixedSelect <class 'type'>
SPIFixedWordSize <class 'type'>
SPIInvalidClockMode <class 'type'>
SPIInvalidWordSize <class 'type'>
SPISoftwareFallback <class 'type'>
SPIWarning <class 'type'>
Servo <class 'gpiozero.devices.GPIOMeta'>
SharedMixin <class 'type'>
SmoothedInputDevice <class 'gpiozero.devices.GPIOMeta'>
SnowPi <class 'gpiozero.devices.GPIOMeta'>
SourceMixin <class 'type'>
StatusBoard <class 'gpiozero.devices.GPIOMeta'>
StatusZero <class 'gpiozero.devices.GPIOMeta'>
ThresholdOutOfRange <class 'type'>
TimeOfDay <class 'gpiozero.devices.GPIOMeta'>
TonalBuzzer <class 'gpiozero.devices.GPIOMeta'>
TrafficHat <class 'gpiozero.devices.GPIOMeta'>
TrafficLights <class 'gpiozero.devices.GPIOMeta'>
TrafficLightsBuzzer <class 'gpiozero.devices.GPIOMeta'>
TrafficpHat <class 'gpiozero.devices.GPIOMeta'>
ValuesMixin <class 'type'>
ZombieThread <class 'type'>
absolute_import <class '__future__._Feature'>
boards <class 'module'>
compat <class 'module'>
devices <class 'module'>
division <class '__future__._Feature'>
event <class 'type'>
exc <class 'module'>
fonts <class 'module'>
input_devices <class 'module'>
internal_devices <class 'module'>
mixins <class 'module'>
output_devices <class 'module'>
pi_info <class 'function'>
pins <class 'module'>
print_function <class '__future__._Feature'>
spi_devices <class 'module'>
str <class 'type'>
threads <class 'module'>
tones <class 'module'>
unicode_literals <class '__future__._Feature'>

可見 gpiozero 提供了非常多類別, 其中常用者如下表所示 : 


 常用類別 說明
 LED LED(pin) 建立物件, 呼叫 on() 與 off() 開關 LED, toggle() 交替
 LEDBoard LEDBoard(pin1, pin2, ...) 建立物件, on() 全亮, off() 全暗, value 屬性個別控制明滅
 Buzzer Buzzer(pin) 建立物件, 呼叫 on() 發聲, off() 停止, beep() 發嗶聲 
 Button Button(pin) 建立物件, 呼叫 wait_for_press() 暫停並監視是否按下, 是才繼續執行
 MotionSensor MotionSensor(pin) 建立物件, 呼叫 motion_detected() 傳回 True/False (PIR 偵測)
 PWMLED PWMLED(pin) 建立物件, 利用其 value 屬性設定 LED 亮度 (0~1, 1 最亮)
 LightSensor LightSensor(pin) 建立物件, 利用其 value 屬性讀取亮度 (0~1, 1 最亮) 
 MCP3008 MCP3008(channel) 建立物件, 利用其 value 屬性讀取該頻道之類比輸入
 ... 
 ... 


使用 gpiozero 時通常用 from gpiozero import xxx, yyy, zzz 方式匯入所需要的類別, 以前一篇的 LED 明滅控制為例, 先呼叫類別建構函式 LED() 建立 LED 物件, 然後呼叫其 on() 與 off() 即可分別使其亮或滅, 例如下列指令會點亮接在 GPIO2 上的 LED 燈 :

from gpiozero import LED
led=LED(2)    # 用 "GPIO2" 或 "BCM2" 亦可
led.on()

注意, 所有的 gpiozero 函式若直接傳入整數, 那是代表 BCM 編號, 也可以傳入字串例如 "GPIO2" 或 "BCM2", 意思相同. 如果要像 RPi.GPIO 模組那樣使用 BOARD 編號 (1~40) 則要傳入 "BOARD3", 因為 GPIO2 的 BOARD 編號為 3. 


測試 3-1 : 用 gpiozero.LED() 控制 LED 燈明滅 [看原始碼]

# rpi-gpio-test-3-1.py
from gpiozero import LED
from time import sleep

led=LED(2)    # GPIO2 (BOARD 編號 3)
n=10
while n>0:
    print('ON')
    led.on()    
    sleep(0.5)
    print('OFF')
    led.off()    
    sleep(0.5)    
    n -= 1

結果與前一篇用 RPi.GPIO 是一樣的. 

下面是使用 gpiozero 控制四個 LED 走馬燈的例子 :


測試 3-2 : 用 gpiozero.LED() 控制四個 LED 跑馬燈 [看原始碼]

# rpi-gpio-led-test-3-2.py
from gpiozero import LED
from time import sleep

led_pins=[2, 3, 4, 17]
leds=[]
for i in led_pins:
    leds.append(LED(i))

n=10
while n>0:
    for i in range(4):
        print(f'LED{i} is ON')
        leds[i].on()  
        sleep(0.5)
        print(f'LED{i} is OFF')
        leds[i].off()    
        sleep(0.5)    
    n -= 1

結果如下 (與 RPi.GPIO 版的一樣) :




沒有留言 :