用樹莓派打造環境監測器[03]:程式讀取感測器讀數、通知功能
目錄
Terrarium Sensor - This article is part of a series.
啟用 I2C 介面 #
樹莓派預設不啟用,但感光器和螢幕都會用到,需要先手動啟用。
$ sudo raspi-config
打開樹莓派系統設定 GUI,並參照下圖打開 I2C。


啟用後透過 $ ls /dev/i2c*
,可發現 /dev
下多了兩個設備。
$ sudo i2cdetect -y 0
或 $ sudo i2cdetect -y 1
:檢查在 I2C bus 上設備的位址,Rev 1 的 I2C bus 是使用 i2c-0;Rev 2 則是 i2c-1。
Python 開發環境設定 #
考慮到一台電腦內可能需要執行許多專案,專案所需的套件、套件版本、python 本身的版本都不盡相同。
若想要有便於管理的環境,最需要的工具有:
- 程式碼版本控制:使用 git,在安裝樹莓派作業系統時已經有內建
- 套件安裝的虛擬環境管理:Poetry
- 套件相依性管理:Poetry
- Python 本身的版本管理:pyenv
安裝 Poetry、pyenv #
先跟著 部落格文章 第三、四章節安裝 Poetry + pyenv
延伸閱讀。選用 Poetry 的原因、使用相關的文章可以參考:
建立專案 #
跟著上述文章步驟安裝完後,執行 $ pyenv --version
、$ poetry --version
確認是否安裝成功。
安裝成功後,照順序執行以下指令建立專案:
$ mkdir <project-name>
:建立專案資料夾$ cd ./<project-name>
:移動到資料夾內$ git init
:初始化 git$ pyenv install 3.10.10
:安裝特定版本 python 到此電腦內,若已經透過 pyenv 安裝過則可省略此指令$ pyenv local 3.10.10
:指定 pyenv 以後在此資料夾內自動使用 3.10.10 的 python$ poetry init
:初始化 poetry,建立pyproject.toml
- 修改
pyproject.toml
:將[tool.poetry.dependencies]
的 python 版本指定為^3.10.10
$ poetry env use 3.10.10
:使 poetry 建立 3.10.10 的虛擬環境,相關內容在.venv
資料夾內
執行完以上指令後,就能以 3.10.10 版本的 python 進行開發。
若是在自己電腦上開發,可以把程式碼 push 到 github 之後在樹莓派 pull 下來執行。
或是透過 SFTP 將整個專案資料夾直接傳到樹莓派內。
也可以透過 VS Code 的 Remote SSH 套件編輯樹莓派上的檔案。
git clone 已存在的專案 #
若是 $ git clone
已存在的專案,則只需要建立虛擬環境並安裝套件:
$ git clone <project-url>
:使用 git 複製專案到本地$ cd <project-name>
:進入專案資料夾$ pyenv install 3.10.10
:安裝特定版本 python 到此電腦內,若已經透過 pyenv 安裝過則可省略此指令$ pyenv local 3.10.10
:指定 pyenv 以後在此資料夾內自動使用 3.10.10 的 python$ poetry env use 3.10.10
:使 poetry 建立 3.10.10 的虛擬環境,相關內容在.venv
資料夾內$ poetry shell
:進入虛擬環境$ poetry install
:使 poetry 安裝套件
其他指令 #
$ poetry shell
:進入虛擬環境,進入後才能使用 poetry 新增、移除套件,要離開則是輸入$ exit
$ poetry add <module-name>
:新增套件$ poetry remove <module-name>
:移除套件$ poetry show --tree
:列出安裝的套件$ pyenv versions
:列出已安裝的 python 版本$ pyenv local
:顯示目前專案使用的 python 版本$ pyenv global
:顯示目前全域使用的 python 版本
從 AM2302 取得溫濕度紀錄 #
使用 adafruit-circuitpython-dht 套件。
$ sudo apt-get install libgpiod2
:安裝adafruit-circuitpython-dht
套件的依賴- 在專案資料夾內
$ poetry shell
:進入虛擬環境 $ poetry add adafruit-circuitpython-dht
:安裝需要的套件- 新增
dht22_demo.py
,取得溫濕度:
完整程式碼可參考 Github Repo
使用的是 BCM 腳位,而不是實體腳位。
例如我使用的 GPIO 6 對應的實體腳位是 31。
dhtDevice = adafruit_dht.DHT22(board.D6) # 使用 BCM 腳位
try:
temperatureC = dhtDevice.temperature
temperatureF = temperatureC * (9 / 5) + 32
humidity = dhtDevice.humidity
print(
"Temp: {:.1f} F / {:.1f} C Humidity: {}% ".format(
temperatureF, temperatureC, humidity
)
)
except RuntimeError as error:
# DHT22 很容易噴各種錯誤,發生錯誤時過一段時間再重試即可
print(error.args[0])
finally:
dhtDevice.exit()
- 在虛擬環境下,執行
$ python dht22_demo.py
就會在終端顯示溫濕度
從 GY30 取得光度紀錄 #
- 在專案資料夾內
$ poetry shell
:進入虛擬環境 $ poetry add smbus2 RPi.GPIO
:安裝需要的套件- 新增
bh1750_demo.py
,取得溫濕度:
完整程式碼可參考 Github Repo
class BH1750:
def __init__(self) -> None:
# 參考下面註腳圖一
# 預設 I2C 位址
# ADDR 腳位在低電位時是 0x23
# 高電位時是 0x5C
self.DEVICE = 0x23
# 參考下面註腳圖二
# 想要讀取的 bh1750 register 地址
# 不同地址有不同解析度
# 在讀取後會設定成 Power Down mode
self.ONE_TIME_HIGH_RES_MODE_1 = 0x20
# 參考下面註腳圖三
# 感測器採取 Big-Endian,最高有效位數放在記憶體最低位 data[0]
# 將高位數字乘以 256,可以當作位元運算,將高位數左移八位
# 除以 1.2 是感測器設定的 Measurement Accuracy
# 若使用解析度為 0.5 lx 的模式
# 最低有效位數代表的是 2^-1,算法就會不一樣
def convertToNumber(self,data):
result=(data[1] + (256 * data[0])) / 1.2
return(result)
# 從 I2C bus 讀取 16 bit 的數據
def readLight(self,addr=None):
if addr is None:
addr = self.DEVICE
# read_i2c_block_data(i2c_addr, register, length)
data = self.bus.read_i2c_block_data(addr, self.ONE_TIME_HIGH_RES_MODE_1, 16)
return self.convertToNumber(data)
def destroy():
GPIO.cleanup()
bh1750 = BH1750()
lightLevel = bh1750.readLight()
print("BH1750 Light Level :" + format(lightLevel, '.2f') + "lx")
bh1750.destroy()
- 在虛擬環境下,執行
$ python bh1750_demo.py
就會在終端顯示光度
圖一、BH1750 在 I2C bus 上的位址 1
圖二、BH1750 register 位址 2
圖三、BH1750 高解析度模式 3
控制 LCD display #
- 在專案資料夾內
$ poetry shell
:進入虛擬環境 $ poetry add RPLCD
:安裝需要的套件- 新增
lcd_demo.py
,控制螢幕顯示:
完整程式碼可參考 Github Repo
lcd = i2c.CharLCD('PCF8574', 0x27, port=1, charmap='A00', cols=20, rows=4)
lcd.write_string('Hello world')
lcd.crlf()
lcd.write_string('IoT with Torai')
lcd.crlf()
lcd.write_string('Raspberry Pi')
sleep(5)
lcd.backlight_enabled = False
lcd.close(clear=True)
- 在虛擬環境下,執行
$ python lcd_demo.py
就會在螢幕顯示訊息
時間對溫濕度的紀錄圖 #
將資料上傳到 ThingSpeak,可以在網頁上瀏覽他們的圖表。
apiKey 是比較私密的資料,透過 dotfile 來存取。
也要設定 .gitignore
避免被 git 記住。
import os
from datetime import datetime
import requests
apiKey = os.getenv('THING_SPEAK_API_KEY')
temp = "26 C"
humidity = "15 %"
lightLevel = "1500 lux"
requests.get(f"https://api.thingspeak.com/update?api_key={apiKey}&field1={temp}&field2={humidity}&field3={lightLevel}")
設定 crontab #
設定每 15 分鐘執行一次
$ crontab -e
:編輯自己的 crontab- 在檔案最後加入
*/15 * * * * /path/to/virtual/env/.venv/bin/python /path/to/terrarium-sensor/main.py
$ crontab -l
:查看自己的 crontab$ grep -i cron /var/log/syslog
:可查看 crontab 執行紀錄
完成參考 #


參考資料 #
- 最佳 Python 套件管理器——Poetry 完全入門指南
- DHT Humidity Sensing on Raspberry Pi or Beaglebone Black with GDocs Logging
- 讓你的 Raspberry Pi 透過 I²C 讀取 eeprom
- Digital 16bit Serial Output Type Ambient Light Sensor IC
- Create pi底板系列:数字光强度传感器模块.No7
- Big-Endian 與 Little-Endian 的差異與判斷程式碼
- smbus2 readthedocs
- Big-Endian 與 Little-Endian 的差異與判斷程式碼
- Guide to Setup Raspberry Pi with LCD Display using I2C Backpack
- Interfacing 20x4 LCD with Raspberry Pi 4 for Creating Custom Character and Scrolling Text
- RPLCD readthedocs
- Linux 設定 crontab 例行性工作排程教學與範例
- How to set virtualenv for a crontab?
↩︎圖一、BH1750 在 I2C bus 上的位址。來源: Digital 16bit Serial Output Type Ambient Light Sensor IC ↩︎圖二、BH1750 register 位址。來源: Digital 16bit Serial Output Type Ambient Light Sensor IC ↩︎圖三、BH1750 高解析度模式。來源: Digital 16bit Serial Output Type Ambient Light Sensor IC