Měření teploty pomocí 1-wire teploměrů a RaspberryPi

Přestože návodů na zprovoznění 1-wire teploměrů založených na DS18B20 je plný internet, myslím si, že praktické zkušenosti s více teploměry a následným zpracování dat se mohou hodit.

Čip DS18B20

Teploměry založené na čipu DS18B20 jsou velice populární především kvůli možnosti zapojit na jeden datový vodič množství teploměrů a dalších čipů podporujících 1-wire. V praxi je sice možné použít parazitické napájení a tím pádem mít pouze datový vodič a zemní vodič (více třeba zde), ale toto zapojení není příliš spolehlivé.

Čidlo DS18B20 s konektorem RJ45

Já jsem ve své instalaci využil tato čidla dodávaná jako součást platformy UniPi. Jedná se o vodotěsné teploměry s kabelem a nakrimpovaným RJ45 konektorem. Přestože čidla jsou výrazně dražší než čínské sety 10 za 1$, ale všechna čidla, která mám při umístění na stejné místo ukáží na desetinu stejnou teplotu, což u čínských odpadů z výroby nemusí být pravda. Výhoda je, že čidla jsou zapojeny čtyřmi vodiči (+5V, GND, 2x data), přičemž oba datové vodiče (žlutý a černý) jsou v pouzdře propojeny:

Díky tomu je možné vytvořit 1-wire kruhovou topologii, přestože fyzicky se jedná o hvězdu. Při propojování sousedních čidel propojím pin 7 s pinem 6 následujícího čidla. Je také možné použít rovnou 1-wire hub, který toto projení zrealizuje. Kruhová topologie přináší vyšší spolehlivost oproti hvězdicové, ve které při delších vedená nastává množství odrazů a spolehlivost 1-wire komunikace jde rapidně dolů.

Připojení k RaspberryPi

Předpokládejme, že čidla máme propojena. Nyní jak vše připojit k RaspberryPi? Jedna z možností je použít dedikovaný I2C řadič, elegantnější cesta a pro připojení teploměrů zcela jistě vyhovující je však možnost připojit 1-wire přímo na GPIO piny našeho Raspberry:

 

Připojení čidel DS18B20 k RaspberryPi

Je vidět, že senzory jsou napájeny +5V, jeden konec logické kružnice je připojen na GPIO pin 4 (žlutý vodič z pinu 7 konektoru RJ45), druhý konec (černý vodič z pinu 5) pak přes pull-up rezistor na +5V. Hodnotu pull-up rezistoru je dobré určit experimentálně tak, aby byla spolehlivě čtena všechna čidla. U mě byla rozumná hodnota 4k7.

Software

Nyní již bychom měli být schopni přečíst teploty poskytované jednotlivými teploměry. Nejprve je nutné si nakonfigurovat Linux na vašem RaspberryPi. Do souboru /boot/config.txt přidáme řádek:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dtoverlay=w1-gpio
dtoverlay=w1-gpio
dtoverlay=w1-gpio

Dále načteme jaderné moduly pro 1-wire, buď z konzole, permanentně například přidáním následujících řádek do souboru /etc/rc.local (to můžeme vypustit sudo před příkazy):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
sudo modprobe w1-gpio
sudo modprobe w1-therm
sudo modprobe w1-gpio sudo modprobe w1-therm
sudo modprobe w1-gpio
sudo modprobe w1-therm

Nyní se přepneme do adresáře /sys/bus/w1/devices/, kde jsou pro každé 1-wire zařízení symlinky na adresáře ve tvaru:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
28-0000074b914c
28-0000074bcf7a
28-0000074be7b1
28-0000074c293a
28-0000074c6fcd
28-0000074cd977
28-000007c22404
28-000007c2951b
w1_bus_master1
28-0000074b914c 28-0000074bcf7a 28-0000074be7b1 28-0000074c293a 28-0000074c6fcd 28-0000074cd977 28-000007c22404 28-000007c2951b w1_bus_master1
28-0000074b914c
28-0000074bcf7a
28-0000074be7b1
28-0000074c293a
28-0000074c6fcd
28-0000074cd977
28-000007c22404
28-000007c2951b
w1_bus_master1

Název každého podadresáře (symlinku) obsahuje jednoznačný identifikátor vypálený v každém DS18B20. Data pak čteme z těchto podadresářů z textového souboru w1_slave, například pomocí následující dvojice Python funkcí:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def read_w1_temp(id):
fn = '/sys/devices/w1_bus_master1/{}/w1_slave'.format(id)
try:
with file(fn) as fr:
data = fr.read()
return data
except (OSError, IOError):
return None
def parse_w1_temp(w1_data):
w1_data = w1_data.splitlines()
line0 = w1_data[0].strip()
if not line0.endswith('YES'):
return None
line1 = w1_data[1].strip()
temp = line1.split('t=')[1]
temp = float(temp)/1000.
return temp
def read_w1_temp(id): fn = '/sys/devices/w1_bus_master1/{}/w1_slave'.format(id) try: with file(fn) as fr: data = fr.read() return data except (OSError, IOError): return None def parse_w1_temp(w1_data): w1_data = w1_data.splitlines() line0 = w1_data[0].strip() if not line0.endswith('YES'): return None line1 = w1_data[1].strip() temp = line1.split('t=')[1] temp = float(temp)/1000. return temp
def read_w1_temp(id):
    fn = '/sys/devices/w1_bus_master1/{}/w1_slave'.format(id)
    try:
        with file(fn) as fr:
            data = fr.read()
            return data
    except (OSError, IOError):
        return None

def parse_w1_temp(w1_data):
    w1_data = w1_data.splitlines()
    line0 = w1_data[0].strip()
    if not line0.endswith('YES'):
        return None

    line1 = w1_data[1].strip()
    temp = line1.split('t=')[1]
    temp = float(temp)/1000.
    return temp

První z funkcí přečte data pro dané ID teploměru, navrácená data pak zpracuje druhá funkce, která v případě, že přečtená data jsou platná vrátí výslednou teplotu jako float.

Závěr

Postavit síť 1-wire teploměrů není nic složitého, ale je třeba dbát na množství detailů – především dodržet kruhovou topologii sítě a použít kvalitní čidla.

Můžete si také přečíst můj předchozí příspěvek o chytře postaveném hloupém domu. A pokud máte další zkušenosti, podělte se o ně na Twitteru: