Introduction to IoT, Spring 2024 Latest Update:2024/06/14
物聯網概論 112學年度 第二學期
高雄市新莊高中多元選修課程
上課教室: 高雄市新莊高中電腦教室
上課時間: 星期一 13:05~15:00
作者:朱克剛
出版社:碁峰
ISBN:9789865027162
出版日期:2021/01/21
樹德科大 圖書館 電子書(登入後可以線上閱讀)
**講義可從以下網頁下載
01週 [02/19]
Raspberry Pi 樹莓派官方網站介紹,Python 教學 ( 學習導讀 )
如何在 Raspberry Pi 上擷取畫面?
Raspberry pi4 OS 已內建 scrot 螢幕擷取工具軟體
開啟 Raspberry終端,在命令列模式下,輸入sudo scrot -v, 如果你已經安裝了這個軟體,則會顯示已經安裝的scrot的版本編號
如果返回sudo: scrot: 找不到命令的話,說明你沒有安裝這個軟體,那麼就需要單獨安裝這個軟體。在命令列中輸入
sudo apt-get install scrot
樹莓派(Raspberry Pi)會通過網路自動下載並安裝scrot軟體,耐心等待直到安裝成功。關閉終端,並重新開啟輸入sudo scrot -v,確認scrot已經安裝成功。
如何使用scrot軟體:
(1) 擷取整個螢幕: 直接按鍵盤上是Prt Sc鍵則可,截取出來的影像會放在/home/pi目錄下,如下圖所示,檔名會以:年-月-日-時分秒-影像大小_scrot.png的形式存檔,放在/home/picture目錄下。
(2) 框定範圍截圖: 開啟終端,並移動終端到不擋住你要截圖的部分的位置,在終端中輸入scrot -s, 然後用滑鼠拖動框住你要截圖的部分則可。影象以年-月-日-時分秒-影像大小_scrot.png的形式存檔,放在/home/picture目錄下。
02週 [02/26]
安裝樹莓派OS,可以參考網站1 (內有安裝中文輸入法說明),網站2
完成開機後請更新樹莓派
1. 更新 /etc/apt/sources.list 底下的套件清單。
$ sudo apt-get update
2. 比對套件清單決定是否需要更新,但如果要更新的套件有相依性問題,則放棄更新。
$ sudo apt-get upgrade
3. 會處理新版本套件與相依性套件的衝突,並試著安裝/移除有問題的套件來完成更新。
$ sudo apt-get dist-upgrade
4. 刪除之前因為有相依性而安裝,但現在已經不再使用的套件(非必要)。
$ sudo apt-get autoremove
5. 清除下載到 /var/cache/apt/archives 的 .deb 套件檔(非必要)
$ sudo apt-get autoclean
樹莓派 GPIO 介紹,可以參考網站 1
Python+IoT 零知識初體驗: GPIO 輸出實驗
推薦使用樹莓派內建的 Thonny Python IDE 編寫程式
程式碼參考
#Thonny Python IDE 編寫程式後,按下 Run 即可執行:
import RPi.GPIO as GPIO
import time
pinLED = 21
GPIO.setmode(GPIO.BCM)
GPIO.setup(pinLED,GPIO.OUT)
try:
while True:
GPIO.output(pinLED,1) #LED On
time.sleep(1)
GPIO.output(pinLED,0) #LED off
time.sleep(1)
except KeyboardInterrupt:
pass
GPIO.cleanup()
#切記: 一定要按 Ctrl+C 讓程式釋放 GPIO 後,才能結束程式
03週 [03/04]
Python+IoT 零知識初體驗: GPIO 輸入實驗
偵測按鈕按下之程式碼參考(使用內建下拉電阻): 即 按鈕按下去後,GPIO17收到3.3V高電位訊號
#Thonny Python IDE 編寫程式後,按下 Run 即可執行:
import RPi.GPIO as GPIO
import time
pinBN =17
GPIO.setmode(GPIO.BCM)
GPIO.setup(pinBN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
try:
n=1
while True:
if GPIO.input(pinBN):
print("{}:按鈕按下".format(n))
n += 1
time.sleep(0.01)
except KeyboardInterrupt:
pass
GPIO.cleanup()
#切記: 一定要按 Ctrl+C 讓程式釋放 GPIO 後,才能結束程式
04週 [03/11] 陳老師有校內會議,當天課程委請 洪詮盛 老師代課
Python+IoT 零知識初體驗: GPIO 輸入+輸出 整合實驗
按鈕按下後,LED 亮10下之程式碼參考(使用內建下拉電阻):
#Thonny Python IDE 編寫程式後,按下 Run 即可執行:
import RPi.GPIO as GPIO
import time
pinBN =17
pinLED = 21
GPIO.setmode(GPIO.BCM)
GPIO.setup(pinBN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(pinLED,GPIO.OUT)
try:
n=1
while True:
if GPIO.input(pinBN):
print("{}:按鈕按下,LED亮10下".format(n))
for num in range(0,10):
GPIO.output(pinLED,1) #LED On
time.sleep(0.5)
GPIO.output(pinLED,0) #LED off
time.sleep(0.5)
n += 1
time.sleep(0.01)
GPIO.output(pinLED,0) #LED off
except KeyboardInterrupt:
pass
GPIO.cleanup()
#切記: 一定要按 Ctrl+C 讓程式釋放 GPIO 後,才能結束程式
05週 [03/18] (段考一: 3/21~3/22)
今天不使用樹莓派硬體套件,我們回顧下前幾週上課用到的 Python 程式語法,了解之後、才能進一步開發出有趣的樹莓派物聯網應用
預祝同學本週段考順利!
06週 [03/25] 陳老師有校內會議,當天課程委請 洪詮盛 老師代課
以下是學習 Python 「一定要會」的基本語法,包含變數、各種型別、邏輯、迴圈...等,幾乎所有的 Python 程式範例,都是由這些基本語法所架構 ( 學習基本語法的過程中可能較為枯燥,只要撐過這個階段,就可以看見 Python 程式語言的美好 )
紅綠燈的常識: 紅綠燈的秒數規定主要受《道路交通標誌標線號誌設置規則》第212條與第231條的規範。這些規定確保了交通燈號的基本運作模式和時序,但實際的秒數設定會根據路況、車流量等因素進行適當調整以適應不同的交通環境。
基本的秒數規定如下:
三色燈號(紅、黃、綠燈):燈號會依序循環顯示綠燈、黃燈、紅燈。
二色燈號(紅、綠燈):燈號會依序循環顯示綠燈、閃光綠燈、紅燈。
黃燈秒數根據速限不同而有所不同:
速限50公里/小時以下的路段,黃燈顯示3秒。
速限在51至60公里/小時的路段,黃燈顯示4秒。
速限61公里/小時以上的路段,黃燈顯示5秒。
全紅秒數:雖然沒有明確的全國性規定,但通常設置為2秒左右,以確保燈號在轉換時的安全。
此外,還有一些補充措施和調整:
部分路口採用智慧型動態號誌系統,根據實際車流自動調整紅綠燈的秒數。
行車倒數計時顯示器會提供剩餘紅燈或綠燈的秒數,幫助駕駛員做出更好的判斷。
地方政府有權在不違反國家規定的前提下,根據當地實際情況自行調整紅綠燈秒數。
# LED 紅綠燈 程式 第一版
# 三色燈號(紅、黃、綠燈):燈號會依序循環顯示綠燈、黃燈、紅燈。
import time
try:
while True:
print("Green LED: 10_sec") #綠燈亮 10秒
time.sleep(10)
print("Yellow LED: 3_sec") #黃燈亮 3秒
time.sleep(3)
print("Red LED: 7_sec") #紅燈亮 7秒
time.sleep(7)
except KeyboardInterrupt:
pass
07週 [04/01]
相對濕度(Relative Humidity, RH),以百分比表示。定義為: 指單位體積空氣中,實際水蒸氣的分壓與相同溫度和體積下水飽和蒸氣壓的百分比。舉個簡單的例子就是,假設一個箱子裡最大可以容納100個神奇寶貝球,但是目前箱子就只有60個寶貝球,因此相對寶貝球數就是60/100,為60%。其中60個寶貝球就是實際水蒸氣分壓,最多可容納的100顆寶貝球就是飽和蒸氣壓。
透過上述例子大家應該對於相對濕度有了一定的輪廓,相對濕度對於人類生活有很大的影響,例如:
若是環境溫度高而造成濕度低,則會刺激呼吸道黏膜。
濕度高會影響人體排汗,汗水無法有效蒸發而使得全身黏膩,這也就是為何夏天時流汗會讓人有不舒服的感受。
濕度太低則會造成皮膚乾裂、脫皮的情況。
濕度高達70%以上的環境非常適合霉菌生長,造成物品發霉、損壞。
電子設備的保存空間有一定濕度的要求,太高會造成電路板、金屬零件的損壞。
室內濕度過高則會造成壁癌,影響居家美觀。
人體最適宜的濕度為50%到70%,低於50%就易有過敏、發癢、皮膚粗糙現象發生,高於70%則易有細菌滋生、誘發氣喘等問題。
今天嘗試寫一個 Python 程式,由相對濕度的百分比數值,決定燈號。定義如下:
濕度低於50%,亮黃燈
濕度介於50%~70%,亮綠燈
濕度高於70%,亮紅燈
# RH 相對溼度 程式 第一版
# 濕度 RH 低於50%,亮黃燈
# 濕度 RH 介於50%~70%,亮綠燈
# 濕度 RH 高於70%,亮紅燈
input1 = input('請輸入目前濕度(RH,單位%)數值: ')
RH = float(input1) #轉型為浮點數
check = 0 #濕度數值合理性檢查,0: 不合理,1: 合理
# RH 數值合理性檢查
if RH > 100 or RH < 0:
print('輸入的溼度數值有誤,應介於0~100%之間')
else:
check = 1
if check == 1:
if RH < 50:
print('目前濕度低於50%,亮黃燈')
elif RH >= 50 and RH<= 70:
print('目前濕度介於50%~70%,亮綠燈')
elif RH > 70:
print('目前濕度高於70%,亮紅燈')
print('程式結束')
08週 [04/08] 陳老師有校內會議,當天課程委請 洪詮盛 老師代課
本周練習將03/25完成的電腦版紅綠燈程式,結合樹莓派與實體LED燈,使紅、黃、綠三色LED燈依據程式設定秒數亮燈。
除了樹梅派、小型麵包板(老師提供)外,還需要使用以下零件:
紅色、黃色、綠色 LED 各一個 (老師提供)
220Ω電阻 (紅紅棕) 一個 (老師提供)
杜邦線(公母) 四條 (零件櫃有杜邦線)
紅色、黃色、綠色 LED、電阻、小型麵包板為老師提供,下課後要繳回,下周老師會再帶來繼續使用
程式碼與電路接法如下圖,可以到教師桌前參考實際電路接法
若有零組件與接線問題,可參閱老師之前講義 Python+IoT 零知識初體驗: GPIO 輸出實驗
** 加分題: 如何讓黃燈閃爍呢??
#切記: 一定要按 Ctrl+C 讓程式釋放 GPIO 後,才能結束程式
•電源腳位有 5V (2支)、3.3V (2支)及 Ground (8支)。
•當輸出腳位時,其電壓分為高電位 3.3V、低電位 0V。
•當輸入腳位時,其電壓 < 0.8V 時判斷為低電位、 > 1.3V 時判斷為高電位。
•除了GPIO 2、GPIO 3是固定內接上拉電阻,其他腳位可透過程式設定內接上拉或下拉電阻。
•單一腳位輸出電流 3mA,電流輸出總和不超過 50mA。
請注意: LED 長腳為正極,要接到 GPIO 對應的接腳
09週 [04/15]
DHT22溫溼度感測器實驗 (Part 1): DHT22硬體與驅動程式安裝測試。DHT22是一款多功能、低成本的濕度感測器,其工作電壓為3.3 – 5.5 V,溫溼度信號取樣頻率為每2秒一次,可量測攝氏-40 to 80度的溫度,以及0~100%的溼度,溫溼度量測準確度分別為±0.5 °C 以及 ±1%。
DHT22溫濕度感測器(內含3條母對母杜邦線)接腳定義
左(+): VCC (Power Supply),接任一3.3V Power (接在1號腳位)
中間(OUT): 溫溼度信號,接GPIO (可接在GPIO4 也就是7號腳位)
右(-): Ground接地,接任一GROUND(可以接在6號腳位)
設置Raspberry Pi與DHT22濕度感測器
1. 在開始為Raspberry Pi濕度感測器程式設計之前,首先確保Raspberry Pi上有最新的更新。
開啟 [指令列模式] 可以運行以下兩個命令來更新與安裝套件。(更新需要花一點時間喔)
sudo apt update
sudo apt full-upgrade
2. 套件軟體更新後,需要安裝python 3和 pip套件。並重新啟動(Reboot) Raspberry Pi。(更新需要花一點時間哦)
sudo apt install python3-dev python3-pip
sudo reboot
3. 將Adafruit 的DHT函式庫 (https://github.com/adafruit/Adafruit_CircuitPython_DHT) 安裝到 Raspberry Pi。(安裝需要花一點時間哦)
sudo pip3 install --upgrade adafruit-python-shell
wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/raspi-blinka.py
sudo python3 raspi-blinka.py (此步驟執行後需要重新啟動)
pip3 install adafruit-circuitpython-dht
4. 請使用 Thonny 程式撰寫使用DHT22量測濕度的Raspberry Pi程式碼,請注意,編寫好的程式碼可以存檔到 dht22 目錄下。EX: /dht22/DHT22_Test1.py。除了在Thonny 編譯環境下執行,也可以[指令列模式] 執行 python3 ~/dht22/DHT22_Test1.py ,以下為操作結果圖示。
import time
import adafruit_dht
import board
dht_device = adafruit_dht.DHT22(board.D4, use_pulseio=False)
#由於我們使用 GPIO4 擷取溫溼度信號,因此要設定為“D4”選項。
#use_pulseio=False 代表禁用 PulseIO 函式庫,而採用了其他的方法來讀取 DHT22 感測器的數據。因為在某些平台上,如 Raspberry Pi,PulseIO 函式庫可能不適用,因此需要關閉它並使用替代方法。
while True:
try:
temperature_c = dht_device.temperature
humidity = dht_device.humidity
print("Temp:{:.1f} C Humidity: {}%".format(temperature_c, humidity))
except RuntimeError as err:
print(err.args[0])
except KeyboardInterrupt:
dhtDevice.exit()
#dhtDevice.exit() 用於退出或關閉與DHT22感測器的連接,並釋放相關的資源,避免不必要的資源佔用或其他問題的發生。
time.sleep(2.0)
請忽略 “A full buffer was not returned. Try again.” 以及其他警示訊息,這是 adafruit-circuitpython-dht 函式庫的內部運作訊息
10週 [04/22]
接續上週進行DHT22溫溼度感測器實驗 (Part 2): 量測所得的溫度以不同的LED燈號顯示
結合樹莓派與實體LED燈,使紅、黃、綠三色LED燈依據溫溼度測試結果
程式設定秒數亮燈。
除了樹梅派、小型麵包板(老師提供)外,還需要使用以下零件:
紅色、黃色、綠色 LED 各一個 (老師提供)
220Ω電阻 (紅紅棕) 一個 (老師提供)
杜邦線(公母) 四條 (零件櫃有杜邦線)
DHT22溫溼度感測器、紅色、黃色、綠色 LED、電阻、小型麵包板為老師提供,下課後要繳回(可以不用拆線),下周老師會再帶來繼續使用
程式碼與電路接法如下圖,可以到教師桌前參考實際電路接法
若有零組件與接線問題,可參閱老師之前講義 Python+IoT 零知識初體驗: GPIO 輸出實驗
DHT22溫溼度感測器實驗 (Part 2): 量測所得的溫度以不同的LED燈號顯示之電路接法
import time
import adafruit_dht
import board
import RPi.GPIO as GPIO
pin_RED_LED = 21
pin_YEL_LED = 20
pin_GRE_LED = 26
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin_RED_LED,GPIO.OUT)
GPIO.setup(pin_YEL_LED,GPIO.OUT)
GPIO.setup(pin_GRE_LED,GPIO.OUT)
dht_device = adafruit_dht.DHT22(board.D4, use_pulseio=False)
#由於我們使用 GPIO4 擷取溫溼度信號,因此要設定為“D4”選項。
#use_pulseio=False 代表禁用 PulseIO 函式庫,而採用了其他的方法來讀取 DHT22 感測器的數據。因為在某些平台上,如 Raspberry Pi,PulseIO 函式庫可能不適用,因此需要關閉它並使用替代方法。
while True:
try:
temperature_c = dht_device.temperature
humidity = dht_device.humidity
print("Temp:{:.1f} C Humidity: {}%".format(temperature_c, humidity))
if temperature_c > 33:
print('目前溫度高於33度,亮紅燈')
GPIO.output(pin_RED_LED,1) #紅燈LED On
GPIO.output(pin_GRE_LED,0) #綠燈LED off
GPIO.output(pin_YEL_LED,0) #黃燈LED off
elif temperature_c >= 28 and temperature_c <= 33:
print('目前溫度介於28~33度,亮黃燈')
GPIO.output(pin_YEL_LED,1) #黃燈LED On
GPIO.output(pin_RED_LED,0) #紅燈LED off
GPIO.output(pin_GRE_LED,0) #綠燈LED off
elif temperature_c < 28:
print('目前溫度低於28%,亮綠燈')
GPIO.output(pin_GRE_LED,1) #綠燈LED On
GPIO.output(pin_RED_LED,0) #紅燈LED off
GPIO.output(pin_YEL_LED,0) #黃燈LED off
except RuntimeError as err:
print(err.args[0])
except KeyboardInterrupt:
dhtDevice.exit()
GPIO.cleanup()
#dhtDevice.exit() 用於退出或關閉與DHT22感測器的連接,並釋放相關的資源,避免不必要的資源佔用或其他問題的發生。
time.sleep(2.0)
11週 [04/29]
接續上週進行DHT22溫溼度感測器實驗 (Part 3): 將量測所得的溫溼度數值繪製成折線圖
PS: 新款 Raspberry Pi 5 (Ubuntu 23.10 64bit) (Password: hchs3420103)
修改 4/15 的程式碼,記得另存新檔名
import time
import adafruit_dht
import board
import csv
#新增,引入 逗號分隔值(Comma-Separated Values),CSV 模組
#其檔案以純文字形式儲存表格資料(數字和文字)。純文字意味著該檔案是一個字元序列,不含必須像二進位數字那樣被解讀的資料。
# CSV是一種通用的、相對簡單的檔案格式,被使用者、商業和科學廣泛應用。最廣泛的應用是在程式之間轉移表格資料
dht_device = adafruit_dht.DHT22(board.D4, use_pulseio=False)
#由於我們使用 GPIO4 擷取溫溼度信號,因此要設定為“D4”選項。
#use_pulseio=False 代表禁用 PulseIO 函式庫,而採用了其他的方法來讀取 DHT22 感測器的數據。因為在某些平台上,如 Raspberry Pi,PulseIO 函式庫可能不適用,因此需要關閉它並使用替代方法。
csvfile = "sensor_readings_1.csv"
#新增,設定儲存溫度資料的csv檔檔名
while True:
try:
temperature_c = dht_device.temperature
humidity = dht_device.humidity
print("Temp:{:.1f} C Humidity: {}%".format(temperature_c, humidity))
#====== 此段為新增 =======
timeC = time.strftime("%I")+':'+time.strftime("%M") + ':' + time.strftime("%S")
data = [temperature_c, timeC]
with open(csvfile, "a") as output:
writer = csv.writer(output, delimiter=",", lineterminator ='\n')
writer.writerow(data)
#====== 此段為新增 =======
except RuntimeError as err:
print(err.args[0])
except KeyboardInterrupt:
dhtDevice.exit()
#dhtDevice.exit() 用於退出或關閉與DHT22感測器的連接,並釋放相關的資源,避免不必要的資源佔用或其他問題的發生。
time.sleep(2.0)
接下來我們已經產出了csv檔,所以現在可以使用各種視覺化工具來畫圖,我們來用一個很常見的python matplotlib畫折線圖。
參考台大教學網頁 https://homepage.ntu.edu.tw/~weitingc/fortran_lecture/Lecture_P_2_1Dplot.slides.html
首先先安裝matplotlib
sudo apt-get install python3-matplotlib
再來編緝繪圖的程式碼,並另存成 plot.py
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
ax.set_facecolor('#fff8dc')
ax.set_ylabel('Temperature')
ax.set_xlabel('Time')
def animate(i):
ftemp = 'sensor_readings_1.csv'
with open(ftemp) as fh:
temp = []
timeC = []
for line in fh:
pieces = line.split(',')
degree = float(pieces[0]) # Convert to float
timeB = pieces[1]
temp.append(degree)
timeC.append(timeB)
ax.clear() # Clear the previous plot
ax.plot(timeC, temp, 'o-', linewidth=3.3)
ani = animation.FuncAnimation(fig, animate, interval=6000)
plt.show()
然後執行 plot.py,便可以得到如下的溫度記錄折線圖 (可以點選下載測試用 sensor_readings_1.csv )
12週 [05/06] (段考二: 5/8~5/10)
本周為同學段考時間,今天主要複習 4/15~4/29 的實作,若有問題,可以來講桌前詢問老師
09週 [04/15] DHT22溫溼度感測器實驗 (Part 1): DHT22硬體與驅動程式安裝測試。
10週 [04/22] 接續上週進行DHT22溫溼度感測器實驗 (Part 2): 量測所得的溫度以不同的LED燈號顯示
11週 [04/29] 接續上週進行DHT22溫溼度感測器實驗 (Part 3): 將量測所得的溫溼度數值繪製成折線圖
13週 [05/13]
認識PWM與呼吸燈實驗
使用材料除了樹莓派、小型麵包板(老師提供)外,還需要使用以下零件:
紅色、黃色、綠色 LED 各一個 (老師提供)
220Ω電阻 (紅紅棕) 一個 (老師提供)
杜邦線(公母) 四條 (零件櫃有杜邦線)
紅色、黃色、綠色 LED、電阻、小型麵包板為老師提供,下課後要繳回,下周老師會再帶來繼續使用
脈衝寬度調變 (Pulse Width Modulation, PWM) 是一種常用於電子和電機工程中的技術。它通過調整脈衝的持續時間(即寬度)來調節通過電路的平均電流。
定義
脈衝:在電子學中,脈衝是一種短暫的電流或電壓變化。你可以將其想像為一種快速的電信號,它突然開始,然後迅速結束。脈衝通常具有一定的形狀和持續時間,它們可以是正的(電壓上升)或負的(電壓下降)。脈衝的特點是它們的「快速變化」和「短暫的持續時間」。
脈衝寬度:脈衝寬度是指脈衝的持續時間,即脈衝從開始到結束的時間長度。在PWM中,通過改變脈衝寬度(即調節脈衝持續的時間),可以控制通過電子裝置的平均功率。脈衝寬度的改變意味著改變脈衝中”高”狀態(或”低”狀態,取決於系統設計)的持續時間。
脈衝寬度調變(PWM)
想像一下,你有一個開關,可以迅速地在開和關之間切換。脈衝寬度調變就是在這個開關上玩一個精確的時間遊戲。
這個遊戲的關鍵在於兩個部分:一部分時間開關是開的(讓電流通過),另一部分時間開關是關的(不讓電流通過)。如果開關打開的時間比關閉的時間長,那麼通過的平均電流就會更多,反之亦然。這種開和關的比例決定了電力的輸出。
這種方法的優點在於它非常高效,因為它只在開關轉換時消耗能量,而不是在調節電力輸出時。PWM廣泛應用於各種設備中,從簡單的亮度調節(如LED燈)到更複雜的應用,如電動機速度控制和各種形式的信號處理。
常見的「呼吸燈」就是利用 PWM 作為連續亮度的調控,讓我們逐步了解如何透過PWM做呼吸燈:
我們可以利用 LED 在一個週期性的波形中亮起來的時間比例,來控制眼睛看到的亮度。
決定週期長度: 決定 High 在整個週期的比例
人眼的視覺暫留極限大概是 25Hz,或是 40ms,因此只要閃爍的週期時間比 40ms 短,人眼就看不出來燈在閃,只會覺得恆亮。亮度即是在一個週期中 LED 亮起來的時間的平均(亮度對時間的積分)。
點亮的時間佔整體週期的比例,也就是下圖中的 high time / period,或是 high time / (high time + low time),叫做 duty cycle (中文: 工作週期 ),通常用百分比表示。
工作週期 Duty cycle = High time / (High time + Low time)
100% 就是燈一直都亮,不熄滅,亮度最亮。
50% 就是只有一半的時間亮,亮度會是全亮時的一半。
20% 的話只有五分之一的時間燈是亮的,眼睛看到的亮度就是全亮時的五分之一。因此,藉由調整 duty cycle,我們就可以控制眼睛看到的亮度。
為什麼非要 PWM 不可 ?
切換 GPIO 的方式產生不同 duty cycle 的波形,這個方式叫做 PWM (Pulse Width Modulation)。
這個技術對真實世界的控制是非常重要的。因爲在大部分的數位系統中,都只處理 0 和 1 的訊號,而無法產生介於 0 和 1 之間的電壓。因此我們在控制大部分的物理量時,比方說要控制燈光的亮度,其實就是用 PWM 的方式,很快速的去切換High Low,並且控制 High 所佔的比例,達到調節輸出功率的目的。
Gogoro 的馬達控制器其實也只能輸出 High(全開) 或是 Low(全關),那麼我們油門輕輕轉一點點的時候,要怎麼讓 Gogoro 的馬達在輕輕轉動時,只輸出一點點的功率呢?答案就是使用 PWM!
只要產生一個 high time 佔 1% 的 PWM 波形給馬達,就可以讓馬達只輸出 1% 的功率。
週期也是關鍵! 除了 duty cycle 之外,PWM 控制另一個很重要的參數是 Period,也就是每一次週期波形的時間,它的倒數就是頻率。在控制 LED 亮度時,PWM 頻率低於人眼的視覺暫留頻率,就會讓我們看起來覺得在閃。而控制馬達時,如果 PWM 頻率太低的話,馬達轉起來就會一頓一頓的。因此 PWM 頻率有一個很重要的關鍵,就是要 :超過被控制系統的反應速度
只要眼睛或是馬達對 PWM 的頻率來不及反應,PWM 對他們來說就不是開關開關的波形,而會被抹平成一個在時間上積分 (或平均) 的結果。
我們可以通過RPi.GPIO中的PWM函數來控制輸出的平均電壓。
pwm = GPIO.PWM(num, frequency)
第一個參數為LED GPIO 接腳編號,第二個參數為頻率,這裡要用到頻率(f)和周期(T)的轉換公式:
假設週期為20毫秒,那得到頻率為1/0.02s,即50Hz。如果開關動作的周期如果太長,即頻率太小,那麼人的肉眼就可以辨識出燈的開關狀態,也就是說LED燈看起來就是閃爍的。
pwm.start(dc) 可以用來開啟PWM,參數dc為工作週期,取值範圍為0-100
如果要更改頻率,可以調用ChangeFrequency( )方法:
pwm.ChangeFrequency(frequency)
如果要修改工作週期,可以使用ChangeDutyCycle( )方法:
pwm.ChangeDutyCycle(dc)
當不需要PWM時,可以使用stop( )來停止:
pwm.stop( )
以下是 呼吸燈 程式碼,請使用 Thonny Python 編譯器實作吧!
程式碼與電路接法如下圖,可以到教師桌前參考實際電路接法
=============================================================================================================
import RPi.GPIO as GPIO
from time import sleep
pin_RED_LED = 21 # Specify the GPIO pin for the red LED (GPIO 21)
pin_GREEN_LED = 26 # Specify the GPIO pin for the green LED (GPIO 26)
GPIO.setmode(GPIO.BCM) # Use BCM numbering for GPIO pins
GPIO.setup(pin_RED_LED, GPIO.OUT) # Set the red LED pin as output
GPIO.setup(pin_GREEN_LED, GPIO.OUT) # Set the green LED pin as output
pwm_RED = GPIO.PWM(pin_RED_LED, 100) # Create PWM instance for the red LED, frequency = 100Hz
pwm_GREEN = GPIO.PWM(pin_GREEN_LED, 100) # Create PWM instance for the green LED, frequency = 100Hz
pwm_RED.start(0) # Start PWM for red LED with duty cycle 0
pwm_GREEN.start(0) # Start PWM for green LED with duty cycle 0
try:
while True:
for duty_cycle in range(0, 101, 5):
pwm_RED.ChangeDutyCycle(duty_cycle) # Change duty cycle for red LED
pwm_GREEN.ChangeDutyCycle(100 - duty_cycle) # Change duty cycle for green LED (opposite of red)
sleep(0.1) # Delay for smoother transition
for duty_cycle in range(100, -1, -5):
pwm_RED.ChangeDutyCycle(duty_cycle) # Change duty cycle for red LED
pwm_GREEN.ChangeDutyCycle(100 - duty_cycle) # Change duty cycle for green LED (opposite of red)
sleep(0.1) # Delay for smoother transition
except KeyboardInterrupt:
pass
pwm_RED.stop() # Stop PWM for red LED
pwm_GREEN.stop() # Stop PWM for green LED
GPIO.cleanup() # Clean up GPIO pins
問題: 請同學研究一下如何讓 黃色LED燈也加入呼吸燈 明暗變化??
14週 [05/20]
使用PWM控制 SG90伺服機 (舵機)
使用材料除了樹莓派,還需要使用以下零件:
SG90伺服機 (老師提供,下課後要繳回,下周老師會再帶來繼續使用)
杜邦線(公母) 三條 (零件櫃有杜邦線)
SG90 伺服機 (舵機) 是一種位置(角度)伺服的驅動器,適用於那些需要角度不斷變化並可以保持的控制系統。目前在遙控玩具,如遙控飛機、遙控汽車、遙控機器人中常見。舵機是一種俗稱,SG90其實是一種伺服馬達。
【引線接法】
棕色: GND
紅色: VCC 3~7.2V (建議5V)
橙色: 控制訊號
以下是參考程式碼,請使用 Thonny Python 編譯器實作吧!
VCC 與 GND 用於提供內部直流馬達及控制線路所需的電源,電壓大小差別在於扭力的大小,和轉動速度的快慢。而訊號線則是用來控制馬達的轉動角度,輸入的訊號為 PWM,伺服馬達的角度就是由 PWM 的 Duty Cycle 去控制。
伺服馬達的角度控制一般需要一個20ms左右的脈衝,該脈衝的高電壓部分一般為0.5ms~2.5ms範圍內。以180度角度伺服為例,那麼對應的控制關係是:
0.5ms--------------0度;
1.0ms------------45度;
1.5ms------------90度;
2.0ms-----------135度;
2.5ms-----------180度;
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD) # 設置GPIO模式
servo_pin = 23 # 定義伺服馬達的訊號接腳
GPIO.setup(servo_pin, GPIO.OUT) # 設置伺服馬達引腳為輸出
pwm = GPIO.PWM(servo_pin, 50) # 定義50Hz的PWM信號
pwm.start(0) # 啟動PWM
def set_angle(angle):
duty = angle / 18 + 2 #0度對應5%,180度對應10%
GPIO.output(servo_pin, True)
pwm.ChangeDutyCycle(duty)
time.sleep(1)
GPIO.output(servo_pin, False)
pwm.ChangeDutyCycle(0)
try:
while True:
set_angle(0) # 將伺服馬達轉到0度
time.sleep(1)
set_angle(90) # 將伺服馬達轉到90度
time.sleep(1)
set_angle(180) # 將伺服馬達轉到180度
time.sleep(1)
except KeyboardInterrupt: # 當使用者按下Ctrl+C時,程式停止
pwm.stop()
finally:
GPIO.cleanup()
補充說明為何 duty = angle / 18 + 2:
在控制伺服馬達的角度時,PWM信號的工作週期(Duty Cycle)決定了伺服馬達的轉動角度。SG90伺服馬達通常在1 ms到2 ms的脈衝寬度之間工作,這對應於0度到180度的轉動範圍。在50Hz的PWM信號下,1個週期是20 ms。
1000 ms / 50 Hz = 20 ms
0度時,脈衝寬度約為1 ms,1 ms的脈衝寬度在 20 ms的週期中對應的工作週期為5%(1 ms / 20 ms * 100)
180度時,脈衝寬度約為2 ms,2 ms的脈衝寬度對應的工作週期為10%(2 ms / 20 ms * 100)
工作週期的範圍是5%到10%,即0度對應5%,180度對應10%。為了把角度轉換成對應的工作週期,可以使用線性映射公式:
duty cycle = (angle/180) × (10%−5%)+5%
簡化後的公式就是:
duty cycle = (angle/18) + 2
這個公式是基於將SG90轉動角度從0到180度映射到工作週期從5%到10%的範圍
15週 [05/27]
延續上週使用PWM控制 SG90伺服機 (舵機) 的實驗,修改程式讓SG90伺服機的轉角由使用者輸入控制
使用材料除了樹莓派,還需要使用以下零件:
SG90伺服機 (老師提供,下課後要繳回,下周老師會再帶來繼續使用)
杜邦線(公母) 三條 (零件櫃有杜邦線)
以下是參考程式碼,請使用 Thonny Python 編譯器實作吧!
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD) # 設置 GPIO 模式
servo_pin = 23 # 定義伺服馬達的引腳
GPIO.setup(servo_pin, GPIO.OUT) # 設置伺服馬達引腳為輸出
pwm = GPIO.PWM(servo_pin, 50) # 定義 50Hz 的 PWM 信號
pwm.start(0) # 啟動 PWM
def set_angle(angle):
duty = angle / 18 + 2 #0度對應5%,180度對應10%
GPIO.output(servo_pin, True)
pwm.ChangeDutyCycle(duty)
time.sleep(0.5)
GPIO.output(servo_pin, False)
pwm.ChangeDutyCycle(0)
try:
while True:
# 從使用者那裡獲取轉角輸入
angle = input("請輸入伺服馬達的轉角 (0 到 180 度,輸入 'exit' 結束程式): ")
if angle.lower() == 'exit':
break
try:
angle = float(angle)
if 0 <= angle <= 180:
set_angle(angle)
else:
print("請輸入 0 到 180 度之間的數字。")
except ValueError:
print("無效輸入,請輸入數字或 'exit'。")
except KeyboardInterrupt:
pass
finally:
# 清理 GPIO 狀態
pwm.stop()
GPIO.cleanup()
16週 [06/03] (6/10 放假)
樹莓派 全彩 RGB LED 顏色控制實驗
使用材料除了樹莓派,還需要使用以下零件:
RGB 全彩 LED (零件照片如下,老師提供,下課後要繳回,下周老師會再帶來繼續使用)
杜邦線(母母) 四條 (零件櫃有杜邦線)
RGB 全彩 LED 零件外觀
1.紫光(Violet),波長 380–450 nm
2.藍光(Blue),波長450–495 nm
3.綠光(Green),波長495–570 nm (對人眼而言,最敏感的波長為555 nm )
4.黃光(Yellow),波長570–590 nm
5.橘光(Orange),波長590–620 nm
6.紅光(Red),波長620–750 nm
RGB 全彩 LED 簡介
LED於1950年代末期被發明,1968年由HP開始商業化量產,早期只有單調的暗紅色電子產品指示燈,1993年中村修二突破藍光 LED 技術的障礙後(中村修二獲得2014年諾貝爾物理獎),逐漸衍生出多重色彩,亮度也大幅提高,並以顯示器(Display)、表面黏著型(SMD)等各種封裝型態深入生活中各個層面。
LED 是利用電能直接轉化為光能的原理,在半導體內正負極 2 個端子施加電壓,當電流通過,使電子與電洞相結合時,剩餘能量便以光的形式釋放,依其使用的材料的不同,其能階高低使光子能量產生不同波長的光,人眼所能接受到各種顏色的光,其波長介於 400-780nm,在此區間之外則為不可見光,包括紅外光及紫外光(UV)
多數 LED 被稱為Ⅲ-Ⅴ族化合物半導體,是由Ⅴ族元素(氮 N、磷 P、砷 As 等)與Ⅲ族元素(鋁 Al、鎵 Ga、銦 In 等)結合而成,以與 IC 半導體所使用之矽(Si)等Ⅳ族元素區別。傳統液相磊晶法(Liquid Phase Epitaxy, LPE)與氣相磊晶法(Vapor Phase Epitaxy, VPE),以磷化鎵(GaP)或砷化鎵(GaAs)為基板,用於生產中低亮度 LED 及紅外光 IrDa 晶粒,其亮度在 1 燭光(1000mcd)以下。
有機金屬氣相磊晶法(Metal Organic Vapor Epitaxy, MOCVD)用於生產高亮度 LED,其亮度約在 6000-8000mcd。以AlGaInP 四種元素為發光層材料在砷化鎵(GaAs)基板上磊晶者,發出紅、橙、黃光之琥珀色系,通稱為四元LED;以 GaN 為材料所生產的藍、綠光 LED,則稱為氮化物 LED,一般以藍寶石(Sapphire)為基板。
傳統液相磊晶法(Liquid Phase Epitaxy, LPE)與氣相磊晶法(Vapor Phase Epitaxy, VPE),以磷化鎵(GaP)或砷化鎵(GaAs)為基板,用於生產中低亮度 LED 及紅外光 IrDa 晶粒。
有機金屬氣相磊晶法(Metal Organic Vapor Epitaxy, MOCVD)用於生產高亮度 LED,其亮度約在 6000-8000mcd。以AlGaInP 四種元素為發光層材料在砷化鎵(GaAs)基板上磊晶者,發出紅、橙、黃光之琥珀色系,通稱為四元LED;以 GaN 為材料所生產的藍、綠光 LED,則稱為氮化物 LED,一般以藍寶石(Sapphire)為基板。
以下是 RGB 全彩 LED 參考程式碼,請使用 Thonny Python 編譯器實作吧!
import time
import RPi.GPIO as GPIO
delay_time = 0.05 # (s)
interval = 8
GPIO.setmode(GPIO.BOARD)
R_pin = 33 # R: 33號腳位(第33根pin)
G_pin = 35 # G: 35號腳位(第35根pin)
B_pin = 37 # B: 37號腳位(第37根pin)
GPIO.setup(R_pin, GPIO.OUT)
GPIO.setup(G_pin, GPIO.OUT)
GPIO.setup(B_pin, GPIO.OUT)
R_pwm = GPIO.PWM(R_pin, 800)
G_pwm = GPIO.PWM(G_pin, 800)
B_pwm = GPIO.PWM(B_pin, 800)
def check_RGB_range(R,G,B):
if R < 0 : R = 0
if G < 0 : G = 0
if B < 0 : B = 0
if R > 255 : R = 255
if G > 255 : G = 255
if B > 255 : B = 255
return R,G,B
R,G,B = 255,0,0
state = 0
R_pwm.start(0)
G_pwm.start(0)
B_pwm.start(0)
try:
while(1):
if (state == 0):
R = R
G = G + interval
B = B
if (state == 1):
R = R - interval
G = G
B = B
if (state == 2):
R = R
G = G
B = B + interval
if (state == 3):
R = R
G = G - interval
B = B
if (state == 4):
R = R + interval
G = G
B = B
if (state == 5):
R = R
G = G
B = B - interval
R,G,B = check_RGB_range(R,G,B)
if(R == 255 and G == 0 and B == 0): state = 0
if(R == 255 and G == 255 and B == 0): state = 1
if(R == 0 and G == 255 and B == 0): state = 2
if(R == 0 and G == 255 and B == 255): state = 3
if(R == 0 and G == 0 and B == 255): state = 4
if(R == 255 and G == 0 and B == 255): state = 5
#mapping
R_mapping = int (R / 255 * 100)
G_mapping = int (G / 255 * 100)
B_mapping = int (B / 255 * 100)
R_pwm.ChangeDutyCycle(R_mapping)
G_pwm.ChangeDutyCycle(G_mapping)
B_pwm.ChangeDutyCycle(B_mapping)
print("R,G,B:\t",R,G,B,"\t mapping: ",R_mapping,G_mapping,B_mapping)
if(R_mapping >= 90 and G_mapping < 15 and B_mapping < 5): time.sleep(delay_time)
time.sleep(delay_time)
except Exception as e:
print(e)
finally:
pwm.stop()
GPIO.cleanup()
6/3 嘉獎加分報告: (2024.06.06 更新)
請同學將 16週 [06/03] 樹莓派 全彩 RGB LED 顏色控制實驗的 LED 顏色變化過程與程式碼 RGB 數值的對應關係寫出來,存成文件檔 (word、pdf 、網頁形式等皆可),在 6/16(日) 前 email 給老師 shchen@stu.edu.tw,信件主旨請寫 "112-2 新莊高中 物聯網概論加分報告 王曉明(請改成您的大名)"。
老師 6/17 上課會選出3位報告撰寫優良的同學給予學校嘉獎 1~2 次喔~
17週 [06/17]
樹莓派 超音波測距實作實驗
使用材料除了樹莓派,還需要使用以下零件:
HC-SR04 超音波感測器 (零件照片如下,老師提供,下課後要繳回,下周老師會再帶來繼續使用)
杜邦線[母母] 四條 (零件櫃有杜邦線)
直尺 (非必要)
MH-FMD 有源蜂鳴器 (非必要,若有使用需要搭配杜邦線[母母] 三條)
基礎知識:超音波測距
超音波測距的方式是發射一個電波,當電波遇到物體反射回來,再被測距儀偵測到反射電波,利用來回時間與音波的速度算出距離,計算公式如下:距離 = (音波發射與接收時間差 * 聲音速度V) / 2;
聲音的速度,在一般空氣中約為每秒340公尺,因來回時間要將距離除以二,才是單程的距離。實際的聲音速度決定於好幾個環境因素,其中一個是溫度,計算時,需將環境因素考慮在內,才能更精確計算距離。
V=331m/s +0.6t
(V:空氣中的音速;t:空氣中的攝氏溫度)
在常溫 20 度時, 音速是 331.5+0.6*20=343.5 m/s
超音波測距的原理是利用一個超音波發射器與一個接收器組成的模組來量測音波從發射到收到反射波的時間, 乘以音速即可得到音波往返的距離, 除以 2 即得與反射物體間的距離. 在常溫 20 度下, 音波前進 1 公分約需 58 微秒, 計算如下 :
1 公分=0.01公尺
0.01 公尺=(343.5 公尺/秒*t)/2
此處除以 2 是因為音波花了 t 秒往返走了兩倍距離, 須除以 2 才是單程距離.
t = (0.01*2)/ 343.5 =58us(微秒)
HC-SR04 超音波感測器有4個接腳,分別是Vcc連接3V3電源(PIN-1),GND連接GND接地(PIN-39),Trig接到 PIN-38 (GPIO 20),並把Echo接到 PIN-40 (GPIO 21)。HC-SR04 測距範圍為 2 cm ~ 450cm,精確度: 3mm,輸入電壓 2.4V ~ 5.5V,工作電流 15mA,偵測廣度為15度。
基本工作原理:
1. 利用HC-SR04 超音波測距模組的 TRIG觸發測距,向 Trig pin 送入一個至少 10us 長的方波
2. 收到方波後模組被驅動,從音波發射端連續發出 8 個 40KHz 的方波,並自動檢測是否有信號返回;
3. 若模組有收到回音,會將 Echo pin 輸出高電位,而後復位為低電位
4. 測量 Echo pin 高電位的持續時間,即為超音波從發出到返回的時間
5. 利用 距離 = (音波發射與接收時間差 * 聲音速度V) / 2 公式,即可求得距離
以下是 HC-SR04 超音波測距參考程式碼,請使用 Thonny Python 編譯器實作吧!
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
TRIGGER_PIN = 38
ECHO_PIN = 40
GPIO.setup(TRIGGER_PIN, GPIO.OUT)
GPIO.setup(ECHO_PIN, GPIO.IN)
GPIO.output(TRIGGER_PIN, GPIO.LOW)
v = 343 # (331 + 0.6*20)
def measure() :
GPIO.output(TRIGGER_PIN, GPIO.HIGH)
time.sleep(0.00001) # 10uS
GPIO.output(TRIGGER_PIN, GPIO.LOW)
pulse_start = None
pulse_end = None
while GPIO.input(ECHO_PIN) == GPIO.LOW:
pulse_start = time.time()
while GPIO.input(ECHO_PIN) == GPIO.HIGH:
pulse_end = time.time()
t = pulse_end - pulse_start
d = t * v
d = d/2
return d*100
def measure_average() :
d1 = measure()
time.sleep(0.05)
d2 = measure()
time.sleep(0.05)
d3 = measure()
distance = (d1 + d2 + d3) / 3
return distance
try :
while True:
distance = measure_average()
print("Distance: %.1f (cm)" % distance)
time.sleep(1)
except KeyboardInterrupt:
print("Exception: KeyboardInterrupt")
finally:
GPIO.cleanup()
補充實作:蜂鳴器
蜂鳴器(Buzzer)是產生聲音的信號裝置,有多種型號,常見的應用包括警笛、報警裝置、計算機、印表機等等電子物品。蜂鳴器基本上可分為「有源蜂鳴器」與「無源蜂鳴器」兩種,有源蜂鳴器內有震盪器,接上電源便可持續發聲,但僅能發出單一頻率的聲音,而無源蜂鳴器則可根據不同頻率發出不同的聲音。
MH-FMD 有源蜂鳴器有三個接腳,分別為 VCC、I/O、GND。VCC 接 3V3電源(PIN-17),GND連接GND接地(PIN-14),I/O 接到 PIN-11 (GPIO 17)。當 I/O 為低電壓時,蜂鳴器則會發聲。
以下是 HC-SR04 超音波測距 + MH-FMD 有源蜂鳴器 參考程式碼,請使用 Thonny Python 編譯器實作吧!
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
TRIGGER_PIN = 38
ECHO_PIN = 40
#=================================
Buzzer_PIN = 11
#=================================
GPIO.setup(TRIGGER_PIN, GPIO.OUT)
GPIO.setup(ECHO_PIN, GPIO.IN)
GPIO.output(TRIGGER_PIN, GPIO.LOW)
#==================================
GPIO.setup(Buzzer_PIN, GPIO.OUT)
GPIO.output(Buzzer_PIN, GPIO.HIGH)
#==================================
v = 343 # (331 + 0.6*20)
def measure() :
GPIO.output(TRIGGER_PIN, GPIO.HIGH)
time.sleep(0.00001) # 10uS
GPIO.output(TRIGGER_PIN, GPIO.LOW)
pulse_start = None
pulse_end = None
while GPIO.input(ECHO_PIN) == GPIO.LOW:
pulse_start = time.time()
while GPIO.input(ECHO_PIN) == GPIO.HIGH:
pulse_end = time.time()
t = pulse_end - pulse_start
d = t * v
d = d/2
return d*100
def measure_average() :
d1 = measure()
time.sleep(0.05)
d2 = measure()
time.sleep(0.05)
d3 = measure()
distance = (d1 + d2 + d3) / 3
return distance
try :
while True:
distance = measure_average()
print("Distance: %.1f (cm)" % distance)
#==================================
if distance < 3.5:
GPIO.output(Buzzer_PIN, GPIO.LOW) # 蜂鳴器響
else:
GPIO.output(Buzzer_PIN, GPIO.HIGH) # 蜂鳴器停止響
#==================================
time.sleep(1)
except KeyboardInterrupt:
print("Exception: KeyboardInterrupt")
finally:
GPIO.cleanup()
18週 [06/24] (段考三: 6/25~6/27) 陳老師有校內會議,當天課程委請 洪詮盛 老師代課
本周為同學段考時間,今天主要複習上週 6/17 的實作以及確認學期成績,若有問題,可以來講桌前詢問老師
或是寫 email 到老師信箱 shchen@stu.edu.tw 詢問。PS: 請在 6/26(三) 中午12:00前告知,逾期無法處理。
成績登記 (最後更新日期2024.06.19 PM 7:30)
若成績登記有誤, 請儘速找老師更正