czwartek, 4 października 2012

Raspberry Pi: magistrala 1-Wire i rejestracja temperatury

DS18B20
Rys. #1: Układ DS18B20
zdjęcie
Rys. #2
zdjęcie
Rys. #3
zdjęcie
Rys. #4
zdjęcie
Rys. #5: Kostka
DS18B20
Schemat #6: 2 czujniki

Poniższy opis w dużym stopniu jest oparty na tekście Raspberry Pi odczyt temperatury przez nodejs.

Podłączenie czujników DS18B20 do Raspberry Pi

Czujniki temperatury DS18B20 firmy Dallas należy dołączyć do portu GPIO (styki P1, P6 oraz P7)

Port GPIO

Do wykonania instalacji potrzeba:

Tzw. przewody połączeniowe żeńskie (4 zł za 10 sztuk); w oryginale to się nazywa jumper wire.

REZYSTOR 0,25W 4,7K OHM węglowy (1,50 zł za 100 sztuk).

DS18B20 termometr 1-wire (4,00--5,00 zł za sztukę).

Kostka elektryczna (około 3 zł za 10 sztuk).

Rurka termokurczliwa do izolacji i wzmocnienia połączeń.

Przewód telefoniczny czterożyłowy lub inny podobny.

Trzy wyprowadzenia układu DS18B20 to: masa (GND), linia danych (DQ) i zasilanie (Vdd). Jeżeli położymy układ ,,napisem do góry''/,,stykami do dołu'' (por. rys. #1), to pierwszy styk od lewj (#1) to GND, środkowy (#2) to DQ a prawy (#3) to Vdd.

Styki DQ oraz Vdd należy złączyć za pomocą rezystora 4K7 (4k7 ohm resistor).

Ja to zrobiłem w ten sposób, że obciąłem złącza żeńskie po jednej stronie przewodów połączeniowych i wsadziłem je do kostki elektrycznej. Następnie dolutowałem dwa kilkucentymetrowe przewody po obu stronach rezystora i końce tych przewodów wsadziłem do odpowiednich styków w kostce elektrycznej. W ten sposób połączyłem DQ oraz Vdd za pomocą rezystora (rys. #5).

Styki czujnika DS18B20 dolutowuję do poszczególnych żył przewodu telefonicznego (jedna żyła jest zbędna). Ponieważ DS18B20 jest bardzo mały, to żeby piny się nie stykały izoluję każdą żyłę cienką rurką termokurczliwą (por. zdjęcie #2, umieszczone obok). Żeby ładniej wyglądało, ale przede wszystkim żeby połączenie czujnik-kabel było mocniejsze (żyły są bardzo cienkie) to na cały przewód naciągam szerszą rurkę (por. zdjęcie #3) i też zgrzewam (por. zdjęcie #4). Zgrzewam zapalniczką....

Odpowiednie żyły przewodu telefonicznego z dolutowanym na końcu układem DS18B20 łączę z kostką. Wydaje mi się, że tak jest lepiej--można łatwiej dodać kolejny termometr lub wydłużyć przewód. Układ jest gotowy i można go podłączyć: Vdd do pina P1, GND do pina P6 a DQ do pina P7. Który pin jest P1 jest jasno oznaczone na płytce.

Moja instalacja ma dwa układy DS18B20: jeden do mierzenia temperatury w pokoju a drugi do mierzenia temperatury na zewnątrz (por. rysunek #5/schemat #6).

Aktualizacja systemu (czyli po co robiłem kopię zapasową)

Aby Raspberry obsługiwał 1-Wire należy dokonać aktualizacji systemu, co można wykonać na kilka sposobów. Zgodnie z sugestią ze strony Raspberry Pi odczyt temperatury przez nodejs zdecydowałem się na Hexxeh's easy updater tool:


wget http://goo.gl/1BOfJ -O rpi-update
sudo mv rpi-update /usr/bin/rpi-update
sudo chmod +x /usr/bin/rpi-update

# Na wszelki wypadek:
sudo apt-get install ca-certificates

# rpi-update wymaga zainstalowania gita
sudo apt-get install git-core

Teraz można wykonać aktualizację (zabieg zawsze potencjalnie ryzykowny dlatego lepiej się zabezpieczyć wykonaniem kopii systemu, co opisano tutaj.)


pi@raspberrystar ~ $ sudo rpi-update
Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS
Performing self-update
Autodetecting memory split
Using ARM/GPU memory split of 192MB/64MB
We're running for the first time
Setting up firmware (this will take a few minutes)
Using HardFP libraries
If no errors appeared, your firmware was successfully setup
A reboot is needed to activate the new firmware

Teraz należy dodać do jądra moduły niezbędne do obsługi instalacji pomiaru temperatury:


sudo modprobe w1-gpio
sudo modprobe w1-therm

Uwaga: aby moduły były dodawana automatycznie, przy starcie systemu należy dodać w1-gpio oraz w1-therm do pliku /etc/modules. Mój plik /etc/modules wygląda następująco:


pi@raspberrystar ~ $ less /etc/modules
snd-bcm2835
w1-gpio
w1-therm

Odczyt temperatury

Po podłączeniu ,,magistrali 1-Wire'' (czytaj kabelków z czujnikami DS18B20 w opisany wyżej sposób) wykonanie polecenia ls w katalogu /sys/bus/w1/devices/ powinno ujawnić urządzenia dołączone do magistrali:


pi@raspberrystar ~ $ ls -l /sys/bus/w1/devices/
lrwxrwxrwx 1 root root 0 paź 3 13:45 w1_bus_master1 -> ../../../devices/w1_bus_master1
lrwxrwxrwx 1 root root 0 paź 3 13:58 28-0000032562aa -> ../../../devices/w1_bus_master1/28-0000032562aa
lrwxrwxrwx 1 root root 0 paź 3 19:27 10-000802012743 -> ../../../devices/w1_bus_master1/10-000802012743

W przypadku mojej ,,magistrali'' są to dwa termometry: 28-0000032562aa oraz 10-000802012743.

Do odczytania temperatury korzystamy z pliku w1_slave:


pi@raspberrystar ~ $ cat /sys/bus/w1/devices/28-0000032562aa/w1_slave
60 01 4b 46 7f ff 10 10 b5 : crc=b5 YES
60 01 4b 46 7f ff 10 10 b5 t=22000

Wielkość temperatury to 22,000 stopnie Celsjusza (t=22000).

Aby odczytać dwa termometry na raz, dodać datę i czas odczytu, a następnie zapisać wszystko do pliku LOG można zastosować następujący skrypt (,,prywatne'' skrypty umieszczam w katalogu ~/bin/):


#!/bin/bash
LOG_DIR=/home/pi/Logs/Digitemp
## Data/czas odczytu
echo "@`date "+%Y%m%d%H%M%S"`" >> $LOG_DIR/digitemp.log
## Czujnik nr1
echo "28-0000032562aa `cat /sys/bus/w1/devices/28-0000032562aa/w1_slave | tr '\n' ' '`" >> $LOG_DIR/digitemp.log
## Czujnik nr2
echo "10-000802012743 `cat /sys/bus/w1/devices/10-000802012743/w1_slave | tr '\n' ' '`" >> $LOG_DIR/digitemp.log

Zapis wygląda jakoś tak (przy założeniu, że powyższy skrypt znajduje się w pliku ~/bin/dt2ht.sh):


pi@raspberrystar ~ $ ~/bin/dt2ht.sh
@20121004100001
28-0000032562aa 5f 01 4b 46 7f ff 01 10 9b : crc=9b YES 5f 01 4b 46 7f ff 01 10 9b t=21937
10-000802012743 1e 00 4b 46 ff ff 0d 10 38 : crc=38 YES 1e 00 4b 46 ff ff 0d 10 38 t=14937

Gdzie: @20121004100001 oznacza datę i czas, 28-0000032562aa/10-000802012743 to identyfikatory czujników a zapis w stylu t=21937, to temperatura (21,937 stopni Celsjusza).

Teraz wystarczy dodać odpowiedni wpis do pliku crontab użytkownika pi, aby, przykładowo temperatura była rejestrowana co godzinę (dt2ht.sh to nazwa opisanego wyżej skryptu służącego odczytu temperatury i zapisania wyników do pliku LOG):


pi@raspberrystar ~ $ mkdir ~/Crontab
pi@raspberrystar ~ $ vi ~/Crontab/crontab
# do pliku ~/Crontab/crontab wpisuję jeden wiersz:
# 0 * * * * /home/pi/bin/dt2ht.sh
# zapisuję/kończę pracę vi

# dodanie zawartości ~/Crontab/crontab do zadań crona
pi@raspberrystar ~ $ crontab ~/Crontab/crontab

# sprawdzenie czy wszystko jest OK:
pi@raspberrystar ~ $ crontab -l
0 * * * * /home/pi/bin/dt2ht.sh

Pozostaje tylko wykonanie (np. za pomocą prostego skryptu PeHaPe) jakiś bardziej efektownego sposobu wyświetlania zawartości pliku LOG.

Dopisane 14 października 2012: W zasadzie działa, ale nie do końca. Czasami nie odczytuje temperatury wypisując CRC error, co wygląda następująco:


62 01 4b 46 7f ff 0e 10 03 : crc=03 NO 62 01 4b 46 7f ff 0e 10 03 t=-1250

Nie mam pojęcia czemu tak się dzieje. Jako workaround wymyśliłem kilkukrotny odczyt:


#!/bin/bash

LOG_DIR=/home/pi/Logs/Digitemp

function ReadSensor() {
local sensorId=$1
local WYNIK=""
local SUCCESS=""

## próbuj odczytać max 3 razy
for i in 1 2 3; do
WYNIK=`cat /sys/bus/w1/devices/$sensorId/w1_slave | tr '\n' ' '`
## pole 12 ($12) to wynik odczytu (YES albo NO):
SUCCESS=`echo $WYNIK | awk '{ print $12}'`

if [ "$SUCCESS" = "YES" ] ; then
## zapisuje numer próby oraz odczytaną informację :
echo "$sensorId=$i $WYNIK" >> $LOG_DIR/digitemp.log
## przerwij pętlę odczytano temperaturę:
break
fi
sleep 3;
done

## Trzy próby okazały się nieudane:
if [ $SUCCESS = "NO" ] ; then
echo "$sensorId=? $WYNIK" >> $LOG_DIR/digitemp.log
fi
}

echo "@`date "+%Y%m%d%H%M%S"`" >> $LOG_DIR/digitemp.log

## Czujnik w pokoju:
ReadSensor "28-0000032562aa"

## Czujnik na zewnątrz:
ReadSensor "10-000802012743"

Plik LOG wygląda jakoś tak:


@20121014220001
28-0000032562aa=1 62 01 4b 46 7f ff 0e 10 03 : crc=03 YES 62 01 4b 46 7f ff 0e 10 03 t=22125
10-000802012743=1 0e 00 4b 46 ff ff 0f 10 fd : crc=fd YES 0e 00 4b 46 ff ff 0f 10 fd t=6812

gdzie pierwszy wyraz to numer sensora, znak równości, numer próby w której odczytano temperaturę lub ?, jeżeli temperatury nie odczytano.

7 komentarzy:

  1. Poprawka w odpowiedzi na email od BK wskazujący na błąd w moim wpisie. Zamiast styk P4 powinno być P6 (zdjęcia są OK). Żeby nie mnożyć bytów poprawkę umieściłem w tekście, a komentarz jest tylko "dla porządku"...

    OdpowiedzUsuń
  2. Bardzo pomocny artykuł :-) Jedno pytanie - czy podłączenie większej liczby czujników DS18B20 (np. 20 sztuk) równolegle nie będzie wymagało mocniejszego opornika?

    OdpowiedzUsuń
  3. Cieszę się że się przydał (wpis). Moja wiedza z dziedziny fizyki jest mocno ograniczona ale wydaje mi się że nie. W tej chwili mam np. 6 podłączonych równolegle czujników i wszystko doskonale działa...

    OdpowiedzUsuń
  4. Witam, można wiedzieć na jakie odległości udali sie Panu rozprowadzić czujniki? Zastanawiam ile metrów mozna by wykorzystać przy tym projekcie

    OdpowiedzUsuń
  5. Ten komentarz został usunięty przez autora.

    OdpowiedzUsuń
  6. Nie za dużą. Maksimum to było ze 3 góra (no może 4 metry od komputera.)
    Mam bardziej rozbudowaną instalację:
    http://pinkaccordions.homelinux.org/stats/digitemp.html
    ale wykorzystującą USB zamiast GPIO (no i taki układ https://www.flickr.com/photos/tprzechlewski/4306010587/). Ona ma pewnie +30 metrów łącznie i 9 czujników. Dodać 10 tego się nie da mimo że teoretycznie limit = 256 (chyba). Instalacja był robiona dość "spontanicznie"--różne kable, luty, kostki itp... Podobno jakość kabli/sposób połączenia ma znaczenie ale ja się na tym nie znam...

    OdpowiedzUsuń