본문 바로가기

스마트팩토리?

[라즈베리파이] DS18B20을 이용한 온도 측정

DS18B20은 9비트 ~ 12비트 수준의 해상도로 온도 데이터를 제공할 수 있는 온도 측정 센서로, 다음과 같은 특징이 있다.

  • 1 - Wire 기반으로 데이터 전송, 데이터 + 전원 공급을 단일 선으로 가능
  • 측정 가능 온도 범위: - 55 ℃ ~ +125 ℃
  • 정확도: - 10 ℃ ~ +85 ℃ 범위에서 ± 0.5 ℃

상온 수준에서 꽤 높은 정밀도로 온도를 측정할 수 있는 센서로 볼 수 있다.


1-Wire Protocol

DS18B20의 pinout

DS18B20은 1-Wire 프로토콜로 동작한다. 1-Wire 프로토콜은 하나의 핀(그림에서는 DQ)으로 전원 공급 + 데이터 전송이 가능한 시리얼 버스 시스템으로, 구조가 단순하고 비용이 저렴해 센서 - 디바이스 간 통신에 사용된다고 한다.

master - slave 프로토콜

1-Wire 프로토콜에는 2가지 결선 방식이 있다.

  • 표준 모드: 전원 핀(VDD)을 전원에 연결하는 방식
    • GND - 시스템 그라운드
    • DQ - 마이크로 컨트롤러의 데이터 핀 ( GPIO )
    • VDD - 시스템 전원 (3.0 ~ 5.5V)
  • 파라사이트 모드: 전원 핀(VDD)을 전원에 연결하지 않는 방식
    • GND - 시스템 그라운드
    • DQ - 마이크로 컨트롤러의 데이터 핀 ( GPIO )
    • VDD - 시스템 그라운드

라즈베리파이 포럼을 보면 라즈베리파이에서는 파라사이트 모드가 가능하다는 말도 있고, 안된다는 말도 있다. 직접 구현했다는 사람이 있는 것을 보면 가능한 것 같기는 한데, 회로나 저항에 대해 더 공부해보고 한번 시도해봐야겠다.

파라사이트 모드의 경우 일반 모드에 비해 더 엄격한 요구사항을 가지고 있다고 한다.  https://forums.raspberrypi.com/viewtopic.php?t=216053


온도 측정 실습

아래 링크의 튜토리얼, 커널 문서를 참조했다.

라즈베리 파이 선 연결하기

  • VDD: 전원을 연결하는 핀. 3.3V 전원 핀 아무거나 연결
  • DQ: 데이터를 전송하는 핀. GPIO4에 연결
  • GND: 시스템의 그라운드에 연결하는 핀. GND 핀 아무거나 연결

나는 VDD는 1번, GND는 14번에 연결했다. 이때, 왜 DQ를 GPIO4에 연결해야 하는 것인지 궁금할 수 있다. 혹시라도 GPCLK0(범용 클럭) 기능이 사용되는걸까?

아니다. 단지 라즈베리파이에서 1-wire 프로토콜로 DQ를 연결하는 핀의 기본 값이 GPIO4일 뿐이다. 만약 다른 핀을 사용하고 싶다면 라즈베리파이 5 기준 /boot/firmware/config.txt 내부 내용을 변경하면 된다.

sudo nano /boot/firmware/config.txt

// 설정 파일에서 맨 아래로 내려가기

[all]

dtoverlay=w1-gpio,gpiopin=[원하는 gpio 번호]

// 설정은 부팅 시 적용되므로, 재시작

sudo reboot

GPIO4 대신 GPIO12에 연결한 모습

나의 경우 gpiopin=12로 지정했고, 실제로 잘 동작했다. 이후 실습은 모두 GPIO12 핀과 DQ를 연결한 상태로 진행됬다. 

1-Wire 연결 옵션 켜기

라즈베리파이는 기본 값으로 1-Wire 연결 옵션이 꺼져 있으므로, raspi-config를 통해 인터페이스 설정을 바꿔줘야한다.

sudo raspi-config

위 명령을 치면 아래와 같은 설정 화면을 띄워준다. Interface Options로 들어가자.

위 과정을 진행했으면 Finish로 설정 페이지를 나간 후 한번 재부팅을 해준다.

sudo reboot

연결 확인

재부팅 이후, 아래 명령을 입력하여 w1 통신 중인 디바이스 목록 폴더로 이동하자.

cd /sys/bus/w1/devices

DS18B20이 정상적으로 연결되었다면 해당 폴더에 28- 로 시작하는 폴더를 발견할 수 있다. 해당 폴더는 연결된 DS18B20 장치를 의미한다.

만약 위 과정이 동작하지 않는다면 어딘가 실수를 했거나, 이상이 있을 수 있다. 성공했다는 가정 하에 28~ 폴더로 이동하고, 어떤 파일 또는 폴더 등이 포함되어 있는지 확인해보자.

cd 28*
ls

https://docs.kernel.org/w1/slaves/w1_therm.html

28- 로 시작하는 폴더 내부에는 여러 파일들이 있다. 각 파일이 수행하는 기능이 궁금하면 직접 cat 명령어로 열어보거나, 위에 제시된 커널 공식 문서를 확인해보자. 현재 온도를 측정할 때 사용되는 파일은 크게 2가지가 있다.

  1. w1_slave: 슬레이브 = 온도 센서와 통신하는데 사용되는 파일
  2. temperature: 장치가 측정한 온도 정보를 섭씨로 표현해주는 파일

w1_slave는 장치와 통신하는데 사용되는 파일로, 읽으면 온도 관련 정보를 주고 쓰면 센서의 정밀도를 바꿀 수 있다.

데이터는 2줄로 전달된다. 커널 문서에 따르면 첫 줄은 crc 체크 정보(YES or NO), 두번째 줄은 t= 다음에 온도 정보가 포함된다. 만약 온도 정보를 얻고 싶다면 w1_slave 파일을 읽어 첫줄이 YES인지 확인하고, 2번째 줄에서 t= 이후의 값을 받아 온도를 구하면 된다. 온도는 milli ℃ 단위로 표현되므로, 1000을 나누면 우리가 아는 섭씨를 구할 수 있다.

temperature 파일을 읽어   milli ℃ 단위의 온도만 얻을 수도 있다. 

내가 본 예제들은 w1_slave을 직접 파싱하는 방식으로 구성되어 있었고, 나 역시 해당 방식으로 실습을 진행했다. 하지만 temperature을 통해 섭씨 온도를 바로 얻을 수 있으므로, 현재 시점에서 굳이 파싱할 필요는 없다고 생각한다.

예제 코드

온도에 따라 LED를 켜고 끈다.

import os
import glob
from time import sleep
from gpiozero import LED

led_RED = LED(21)
led_BLUE = LED(20)
led_GREEN = LED(16)

os.system("modprobe w1-gpio")
os.system("modprobe w1-therm")

base_dir = '/sys/bus/w1/devices/'
# 기본값 gpio04와 연결하면 해당 폴더가 생성됨.
device_folder = glob.glob(base_dir + "28*")[0]
# 온도 원시값 정보가 포함되어 있는 폴더
device_file = device_folder + "/w1_slave"
# 

def read_temp_raw():
  
  lines: list[str] = None
  with open(device_file, "r") as f:
    lines = f.readlines()
  return lines

def read_temp():
  lines = read_temp_raw()

  while not "YES" in lines[0]:
    sleep(0.2)
    lines = read_temp_raw()
  
  equals_pos = lines[1].find("t=")

  if equals_pos != -1:
    temp_string = lines[1][equals_pos + 2:]
    temp_c = float(temp_string) / 1000.0
    return temp_c

# 온도 얻기는 ok

while True:
  temp = read_temp()
  if temp >= 30:
    led_RED.on()
    led_BLUE.off()
    led_GREEN.off()
  elif temp >= 20:
    led_RED.off()
    led_BLUE.on()
    led_GREEN.off()
  else:
    led_RED.off()
    led_BLUE.off()
    led_GREEN.on()
  print(temp)
  sleep(2.0)

temperature 폴더 값을 읽는 경우 코드가 매우 간단해진다.

import os
import glob
from time import sleep
from gpiozero import LED

led_RED = LED(21)
led_BLUE = LED(20)
led_GREEN = LED(16)

# 드라이버를 등록
os.system("modprobe w1-gpio")
os.system("modprobe w1-therm")

base_dir = '/sys/bus/w1/devices/'
# 기본값 gpio04와 연결하면 해당 폴더가 생성됨.
device_folder = glob.glob(base_dir + "28*")[0]
# 온도 원시값 정보가 포함되어 있는 폴더
# device_file = device_folder + "/w1_slave"
device_file = device_folder + "/temperature"

def read_temp():
  temperature = 0
  with open(device_file, "r") as f:
    data = f.readline()
    temperature = int(data)
  return temperature / 1000

while True:
  temp = read_temp()
  if temp >= 30:
    led_RED.on()
    led_BLUE.off()
    led_GREEN.off()
  elif temp >= 20:
    led_RED.off()
    led_BLUE.on()
    led_GREEN.off()
  else:
    led_RED.off()
    led_BLUE.off()
    led_GREEN.on()
  print(temp)
  sleep(2.0)