[英]Read multiple DS18B20 temperature sensors faster using Raspberry Pi
我的自定義傳感器儀表板每秒請求新讀數。
這工作得很好,直到我連接了 3 個 DS18B20 溫度傳感器(1 線協議,所以都在 1 個引腳上),每個需要 750 毫秒來提供新數據。
這是我目前用來讀取每個傳感器溫度的類:
# ds18b20.py
# written by Roger Woollett
import os
import glob
import time
class DS18B20:
# much of this code is lifted from Adafruit web site
# This class can be used to access one or more DS18B20 temperature sensors
# It uses OS supplied drivers and one wire support must be enabled
# To do this add the line
# dtoverlay=w1-gpio
# to the end of /boot/config.txt
#
# The DS18B20 has three pins, looking at the flat side with the pins pointing
# down pin 1 is on the left
# connect pin 1 to GPIO ground
# connect pin 2 to GPIO 4 *and* GPIO 3.3V via a 4k8 (4800 ohm) pullup resistor
# connect pin 3 to GPIO 3.3V
# You can connect more than one sensor to the same set of pins
# Only one pullup resistor is required
def __init__(self):
# Load required kernel modules
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')
# Find file names for the sensor(s)
base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '28*')
self._num_devices = len(device_folder)
self._device_file = list()
i = 0
while i < self._num_devices:
self._device_file.append(device_folder[i] + '/w1_slave')
i += 1
def _read_temp(self, index):
# Issue one read to one sensor
# You should not call this directly
# First check if this index exists
if index >= len(self._device_file):
return False
f = open(self._device_file[index], 'r')
data = f.read()
f.close()
return data
def tempC(self, index=0):
# Call this to get the temperature in degrees C
# detected by a sensor
data = self._read_temp(index)
retries = 0
# Check for error
if data == False:
return None
while (not "YES" in data) and (retries > 0):
# Read failed so try again
time.sleep(0.1)
#print('Read Failed', retries)
data = self._read_temp(index)
retries -= 1
if (retries == 0) and (not "YES" in data):
return None
(discard, sep, reading) = data.partition(' t=')
if reading == 85000:
# 85ºC is the boot temperature of the sensor, so ignore that value
return None
temperature = float(reading) / 1000.0
return temperature
def device_count(self):
# Call this to see how many sensors have been detected
return self._num_devices
如果當前溫度讀數尚未完成,我已經嘗試返回之前的溫度讀數,但這並沒有減少讀取傳感器所需的時間,所以我想唯一的方法是異步做事。
我可以降低精度以減少每次讀取所需的時間,但理想情況下,我會在單獨的線程上同時讀取所有傳感器。
我怎樣才能最好地實現這一點? 或者有沒有其他方法可以提高多個 DS18B20 傳感器的讀取速度?
感謝您的任何見解!
您正面臨 Linux 內核驅動程序引入的一些限制。 如果您直接與 OneWire 協議交互,則所有三個傳感器的讀取周期只有一個750ms
,而不是(3 * 750ms)
。 當直接使用 1-wire 協議時,您可以向總線上的所有設備發出一個“轉換溫度”命令,如此處所述,然后讀取所有傳感器。
Linux 驅動程序明確不支持這種操作模式:
如果沒有任何設備是寄生供電的,則可以同時轉換所有設備,然后返回讀取單個傳感器。 目前不支持。 讀取值時,驅動程序也不支持降低精度(這也會減少轉換時間)。
這意味着您被困在每個設備讀取周期 750 毫秒。 您最好的選擇可能是將傳感器讀取代碼放在單獨的線程中,例如:
import glob
import threading
import time
# Note that we're inheriting from threading.Thread here;
# see https://docs.python.org/3/library/threading.html
# for more information.
class DS18B20(threading.Thread):
default_base_dir = "/sys/bus/w1/devices/"
def __init__(self, base_dir=None):
super().__init__()
self._base_dir = base_dir if base_dir else self.default_base_dir
self.daemon = True
self.discover()
def discover(self):
device_folder = glob.glob(self._base_dir + "28*")
self._num_devices = len(device_folder)
self._device_file: list[str] = []
for i in range(self._num_devices):
self._device_file.append(device_folder[i] + "/w1_slave")
self._values: list[float | None] = [None] * self._num_devices
self._times: list[float] = [0.0] * self._num_devices
def run(self):
"""Thread entrypoint: read sensors in a loop.
Calling DS18B20.start() will cause this method to run in
a separate thread.
"""
while True:
for dev in range(self._num_devices):
self._read_temp(dev)
# Adjust this value as you see fit, noting that you will never
# read actual sensor values more often than 750ms * self._num_devices.
time.sleep(1)
def _read_temp(self, index):
for i in range(3):
with open(self._device_file[index], "r") as f:
data = f.read()
if "YES" not in data:
time.sleep(0.1)
continue
disacard, sep, reading = data.partition(" t=")
temp = float(reading) / 1000.0
self._values[index] = temp
self._times[index] = time.time()
break
else:
print(f"failed to read device {index}")
def tempC(self, index=0):
return self._values[index]
def device_count(self):
"""Return the number of discovered devices"""
return self._num_devices
因為這是一個線程,你需要先.start()
它,所以你的代碼看起來像:
d = DS18B20()
d.start()
while True:
for i in range(d.device_count()):
print(f'dev {i}: {d.tempC(i)}')
time.sleep(0.5)
您可以根據需要隨時調用tempC
方法,因為它只是從_values
數組中返回一個值。 實際更新頻率由run
方法中的循環控制(以及傳感器施加的最小循環時間)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.