簡體   English   中英

使用 Raspberry Pi 更快地讀取多個 DS18B20 溫度傳感器

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM