홍창민정보·인공지능 교사

가스·공기 · 난이도 보통

SEN0322 산소 센서를 바깥 공기로 보정하고 0.15%vol 분해능 안에서 호흡 실험하기

DFRobot SEN0322는 전기화학식으로 산소 농도(%vol)를 재는 I²C 모듈이에요. 주소 스위치 0x70~0x73, 보정값 레지스터를 직접 읽는 피코 코드, 바깥 공기 20.9%로 보정하는 순서, 0.15%vol 분해능을 고려한 씨앗·효모·숨 탐구를 정리했어요.

글 홍창민 · 갱신 2026-10-31 · 읽는 데 약 7분 · 가상 실행 확인 (실물 미확인)

이 센서로 무엇을 잴 수 있나

SEN0322는 DFRobot의 Gravity 시리즈 산소 센서예요. 전기화학 원리로 공기 속 산소 농도를 %vol(부피 비율) 단위로 재고, 측정 범위는 0~25%vol, 분해능은 0.15%vol이에요. 전원은 3.3~5.5V, 수명은 2년으로 소개되어 있고, I²C로 값을 보내요.

바깥 공기의 산소는 약 20.9%vol이에요. 공기 100L 속에 산소가 20.9L 들어 있다는 뜻이에요. 이 센서로는 생물이 산소를 쓰거나 내놓아서 생기는 변화를 잴 수 있어요. 싹트는 씨앗의 호흡, 설탕물 속 효모의 활동, 한 번 내쉰 숨과 교실 공기의 차이처럼요. 분해능이 0.15%vol이라 변화가 이보다 작으면 구별되지 않아요. 그래서 실험은 변화가 충분히 커지도록 작은 용기와 넉넉한 시간으로 설계해야 해요.

원리

전기화학식 산소 센서는 산소가 전극에서 반응하며 만드는 아주 작은 전류를 재요. DFRobot 라이브러리에는 이 전류를 µA 단위로 읽는 함수가 따로 있어요. 모듈 안의 마이크로컨트롤러가 이 값을 정리해서 I²C 레지스터에 넣어 두고, 피코는 그것을 읽어요.

레지스터 구조는 DFRobot_OxygenSensor 라이브러리(MIT)에서 확인할 수 있어요. 0x03에서 3바이트를 읽어 b0 + b1/10 + b2/100을 만들고, 여기에 보정 계수 key를 곱하면 산소 농도예요. key는 0x0A에서 2바이트로 읽어 1000으로 나눈 값이고, 0이면 공장 기본값 20.9/120을 써요. 보정은 바깥 공기에서 0x08 레지스터에 농도 × 10(20.9%면 209)을 써 넣으면 돼요. 라이브러리의 보정 예제는 값이 자리 잡는 데 약 3분이 걸린다고 적어 두었어요.

I²C 주소는 모듈 뒷면의 스위치 A0·A1로 정해요. [0 0]이면 0x70, [1 0]이면 0x71, [0 1]이면 0x72, [1 1]이면 0x73이고 기본값은 0x73이에요.

준비물

  • 라즈베리파이 피코와 USB 케이블
  • DFRobot SEN0322 산소 센서와 Gravity I²C 케이블(또는 점퍼선)
  • 뚜껑이 있는 투명 밀폐 용기(뚜껑에 케이블 구멍), 테이프
  • (탐구) 물에 불려 싹을 틔운 콩, 마른 콩, 효모와 설탕, 온도계, 지퍼백

배선

SEN0322 산소 센서를 바깥 공기로 보정하고 0.15%vol 분해능 안에서 호흡 실험하기: 브레드보드에 피코와 센서를 꽂은 배선 그림
시나브로 PICO 브레드보드 작업대와 같은 계산으로 그린 배선이에요. 위 + 줄은 3.3V, 아래 + 줄은 5V예요.
브레드보드 따라 꽂기 순서 (9단계)
  1. 피코를 브레드보드 가운데 홈에 걸쳐 꽂아요. USB 단자가 왼쪽을 보게 하고, 1번 핀이 f2, 40번 핀이 e2에 들어가요. 다리가 홈 양쪽에 모두 들어갔는지 옆에서 봐요.
  2. 주황 선: 피코 36번 핀(3V3(OUT)) 옆 칸 a6 → 위쪽 + 줄(3.3V). 이제 이 줄 전체가 3.3V예요.
  3. 검은 선: 피코 38번 핀(GND) 옆 칸 a4 → 위쪽 − 줄(GND). 이제 이 줄 전체가 GND예요.
  4. 산소 농도 DFRobot SEN0322 꽂기: 다리 VCC · GND · SDA · SCL를 위쪽 a24 · a25 · a26 · a27에 차례로 꽂아요. 다리가 브레드보드에 바로 안 들어가면 암-수 점퍼선으로 같은 칸에 이어요.
  5. 주황 선(3.3V 전원): 산소 농도 VCC 줄 b24 → 위쪽 + 줄(3.3V)
  6. 검은 선(GND): 산소 농도 GND 줄 b25 → 위쪽 − 줄(GND)
  7. 파란 선(신호): 산소 농도 SDA 줄 b26 → 피코 6번 핀(GP4) 옆 칸 j7
  8. 파란 선(신호): 산소 농도 SCL 줄 b27 → 피코 7번 핀(GP5) 옆 칸 j8
  9. 마지막 확인: ① 위쪽 + 줄(3.3V)과 아래쪽 + 줄(5V)이 서로 이어지지 않았나요? ② 모든 부품의 GND가 − 줄에 이어졌나요? ③ 5V 신호 다리는 저항을 거쳐 피코에 이어졌나요? ④ 긴 브레드보드는 + − 줄이 가운데에서 끊긴 것도 있어요(빨간·파란 선이 끊겨 있으면 짧은 선으로 이어요). ⑤ 그림과 한 칸씩 비교한 뒤 USB를 꽂아요.
모듈 핀 피코 핀 비고
VCC(+) 3V3(OUT) (36번) 3.3~5.5V를 받지만 3.3V로
GND(−) GND (38번) 공통 접지
SDA GP4 (6번) I2C0 SDA
SCL GP5 (7번) I2C0 SCL

분압 저항은 필요 없어요. 모듈을 3.3V로 켜면 I²C 신호도 3.3V 안에서 움직여 피코 핀에 안전해요. 주소가 0x70~0x73이라 BME280(0x76 또는 0x77)과 같은 I²C 줄에 함께 연결해도 겹치지 않아요. 온습도까지 같이 기록하고 싶을 때 편해요.

코드

from machine import Pin, I2C
import time

i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=100000)
CALIBRATE = False              # 바깥 공기에서 3분 넘게 켠 뒤 한 번만 True로 실행

found = i2c.scan()
addr = None
for a in (0x70, 0x71, 0x72, 0x73):     # 뒷면 스위치에 따라 넷 중 하나
    if a in found:
        addr = a
if addr is None:
    raise RuntimeError("산소 센서를 못 찾았어요: 배선과 주소 스위치 확인")
print("주소:", hex(addr))

def key():
    k = i2c.readfrom_mem(addr, 0x0A, 2)   # 보정값(×1000). 0이면 공장 기본값
    v = k[1] * 256 + k[0]
    return v / 1000 if v else 20.9 / 120

def oxygen():
    d = i2c.readfrom_mem(addr, 0x03, 3)
    return key() * (d[0] + d[1] / 10 + d[2] / 100)

if CALIBRATE:
    i2c.writeto_mem(addr, 0x08, bytes([209]))   # 20.9 %vol × 10
    print("보정값을 저장했어요. CALIBRATE를 False로 돌려놓으세요")
    time.sleep(1)

avg = oxygen()
while True:
    o2 = oxygen()
    avg = avg * 0.9 + o2 * 0.1             # 최근 값 위주로 묶은 평균
    print(f"DATA: o2={o2:.2f}, o2_avg={avg:.2f}")
    time.sleep(1)
  • i2c.scan()으로 0x70~0x73 가운데 응답하는 주소를 찾아요. 스위치를 바꿔도 코드를 고칠 필요가 없어요.
  • key()와 oxygen()은 라이브러리의 계산을 그대로 옮긴 거예요. 라이브러리 파일을 따로 올리지 않아도 돼요.
  • CALIBRATE를 True로 바꾸고 실행하면 0x08에 209를 써서 지금 공기를 20.9%vol로 보정해요. 한 번 실행한 뒤 반드시 False로 되돌려요.
  • o2_avg는 새 값에 0.1의 무게를 주는 평균이에요. 분해능 근처의 작은 흔들림을 누그러뜨려요.

값 해석과 보정

보정은 바깥 공기나 환기가 잘 되는 창가에서 해요. 센서를 켜고 3분 넘게 두어 값이 멈춘 뒤 CALIBRATE = True로 한 번 실행해요. 그다음부터는 실험을 시작하기 전에 바깥 공기에서 20.9% 근처가 나오는지만 확인해요. 많이 벗어나면 다시 보정해요.

결과는 분해능에 맞춰 읽어요. 0.15%vol보다 작은 차이는 측정의 흔들림과 구별되지 않아요. 예를 들어 처음과 끝이 20.9%와 20.8%라면 "변화가 없거나 측정할 수 없을 만큼 작다"라고 적는 것이 정직해요. 변화를 키우려면 용기를 작게 하거나, 생물의 양을 늘리거나, 시간을 늘려요.

밀폐 용기 실험에서는 시작값과 끝값만 보지 말고 시간에 따른 그래프의 기울기(%vol/분)를 구해요. 기울기는 시작할 때의 작은 차이에 덜 흔들려요. 생물의 활동은 온도에 따라 달라지니, 실험하는 동안의 온도도 함께 적어 두면 결과를 비교하기 쉬워요.

센서 수명은 2년으로 소개되어 있어요. 새 버전 모듈은 0x0E 레지스터로 탐침 수명 상태(1이면 정상, 0이면 교체 권장)를 알려 준다고 라이브러리에 적혀 있어요.

자주 하는 실수

  1. CALIBRATE = True를 그대로 두고 실험하기. 실행할 때마다 그 순간의 공기를 20.9%로 저장해요. 밀폐 용기 안에서 다시 실행하면 결과가 통째로 틀어져요.
  2. 환기 안 된 교실 안에서 보정하기. 바깥 공기 기준이 흔들리면 모든 값이 함께 밀려요.
  3. 0.1% 차이를 결과로 해석하기. 분해능(0.15%vol)보다 작아요.
  4. 켜자마자 측정하기. 값이 자리 잡는 데 약 3분이 걸려요.

이 센서로 잴 수 있는 탐구

1. 싹트는 콩은 산소를 얼마나 쓸까 - 질문: 싹튼 콩과 마른 콩을 같은 용기에 넣으면 산소 감소 빠르기가 다를까? - 바꾸는 것: 콩의 상태(싹튼 콩, 마른 콩, 빈 용기) - 재는 것: 2시간 동안의 o2_avg 감소 기울기(%vol/시간) - 방법: 같은 크기 용기에 같은 개수를 넣고, 센서를 뚜껑에 고정해 테이프로 구멍을 막아요. 같은 온도에서 조건마다 2번 해요. 빈 용기는 센서와 용기 자체에서 생기는 변화를 확인하는 대조군이에요. 빈 용기에서도 값이 움직인다면 그만큼은 콩 때문이 아니에요.

2. 효모는 미지근한 물에서 산소를 더 빨리 쓸까 - 질문: 설탕물 온도에 따라 효모가 산소를 줄이는 빠르기가 달라질까? - 바꾸는 것: 설탕물 온도(찬 수돗물, 온도계로 맞춘 미지근한 물) - 재는 것: 30분 동안의 산소 감소 기울기(%vol/분), 물 온도(℃) - 방법: 효모와 설탕의 양, 물의 양, 용기 크기를 같게 해요. 센서는 액체에 닿지 않게 위쪽에 두고 조건마다 3번 해요.

3. 한 번 내쉰 숨에는 산소가 얼마나 남아 있을까 - 질문: 교실 공기와 내쉰 숨의 산소 농도는 얼마나 다를까? - 바꾸는 것: 공기의 종류(교실 공기, 한 번 내쉰 숨을 담은 지퍼백) - 재는 것: 3분 뒤의 o2_avg(%vol) - 방법: 센서를 지퍼백 안에 넣고 피코는 밖에 둬요. 숨을 한 번만 내쉬어 담고 바로 닫아요. 세 사람이 두 번씩 해요.

안전

  • 산소를 만들지 않아요. 과산화수소를 분해하는 실험은 이 센서의 범위(25%vol)를 넘기고, 진한 산소는 작은 불씨도 크게 키워요.
  • 숨 실험은 내쉬기만 하고, 봉지의 공기를 다시 들이마시지 않아요. 어지러우면 바로 멈춰요.
  • 밀폐 용기에 동물을 넣지 않아요.
  • 이 장치는 인증받은 산소 농도 측정기가 아니에요. 맨홀, 탱크, 지하 공간처럼 산소가 부족할 수 있는 곳의 안전 판단에 쓰지 마세요.

더 알아보기

글쓴이

홍창민 · 울산 학성고등학교 정보·인공지능 교사. 수업에 쓰는 도구(시나브로 PICO 웹 IDE, 학교 AI 서버 등)를 직접 만들어 운영해요. 소개 보기 · 고칠 곳 알려 주기