왜 내 OLED는 얼룩덜룩해질까? 수명을 2배 늘리는 팁 (feat. PIR sensor)

OLED 번인 방지: PIR 센서로 디스플레이 수명 늘리는 법

OLED 디스플레이의 최대 단점인 밝기 저하와 번인 현상! 장시간 화면을 켜두었을 때 발생하는 얼룩덜룩한 잔상을 방지하기 위해, PIR 인체감지 센서를 활용하여 사람이 있을 때만 화면이 켜지게 만드는 스마트한 해결 방법을 소개합니다.


※📱 OLED의 원리와 치명적인 약점: 왜 번인이 생길까?

1. OLED란 무엇인가? (Organic Light Emitting Diode)

OLED는 ‘유기 발광 다이오드’의 약자로, 화면 뒤에서 빛을 쏴주는 백라이트가 필요한 LCD와 달리 소자 하나하나가 스스로 빛을 내는(자발광) 방식입니다.

  • 완벽한 블랙: 빛을 아예 꺼버릴 수 있어 무한대의 명암비를 자랑합니다.
  • 얇은 두께: 백라이트가 없어 디스플레이를 매우 얇게, 심지어 종이처럼 휠 수도 있습니다.

2. 치명적인 단점: 번인(Burn-in)과 수명

하지만 OLED는 **’유기물’**을 태워서 빛을 냅니다. 마치 촛불처럼 켜두면 서서히 타들어 가며 수명이 줄어듭니다.

  • 밝기 불균형: 특정 픽셀만 계속 켜져 있으면 그 부분의 소자가 다른 곳보다 빨리 노화되어 어두워집니다.
  • 잔상(Ghosting): 화면을 꺼도 예전 모습이 얼룩처럼 남는 현상입니다. 특히 고정된 UI(시계, 메뉴바)가 있는 DIY 프로젝트에서 자주 발생합니다.

3. 실제 1~2개월만 계속 켜놔도 OLED가 아래처럼 얼룩덜룩 된 사례

왼쪽: 정상, 중앙:상시 켜논 화면, 오른쪽: 번인현상으로 밝기 불균형

※🛠️ 공학적 해결책: PIR 센서와의 만남

OLED의 수명을 늘리는 가장 확실한 방법은 **”필요할 때만 켜는 것”**입니다. 하지만 매번 버튼을 누르기는 귀찮죠. 이때 PIR(Passive Infrared) 센서가 훌륭한 대안이 됩니다.

PIR 센서란?

적외선을 통해 사람의 움직임을 감지하는 센서입니다. 현관문 자동등에 쓰이는 바로 그 센서입니다!

작동 알고리즘 (PIR + OLED)

  1. 감지 모드: PIR 센서가 주변의 움직임을 감시합니다. (OLED는 Off 상태로 수명 보존)
  2. 활성화: 사람이 다가오면 센서가 신호를 보내고, MCU(아두이노, ESP32 등)가 즉시 OLED를 켭니다.
  3. 자동 종료: 설정한 시간 동안 움직임이 없으면 다시 OLED를 꺼서 소자를 보호합니다.

“OLED의 선명함은 포기할 수 없지만, 번인이 걱정된다면 PIR 센서를 달아보세요. 하드웨어적인 보호 수단 하나가 여러분의 디스플레이 수명을 2배, 아니 10배까지도 늘려줄 것입니다. 이번 프로젝트를 통해 똑똑하고 오래가는 DIY 기기를 만들어보시길 바랍니다!”


1. PIR 센서란? (Passive Infrared)

  • Passive(수동형): 센서가 스스로 빛이나 에너지를 쏘는 게 아니라, 외부에서 들어오는 적외선 에너지를 ‘받기만’ 하기 때문에 붙여진 이름입니다.
  • 작동 원리: 모든 생명체(사람, 동물)는 열을 방출하며, 이 열은 적외선 형태로 나타납니다. PIR 센서 안에는 두 개의 적외선 감지 소자가 있는데, 사람의 움직임으로 인해 두 소자에 들어오는 적외선 양에 차이가 생기면 “사람이 있다!”라고 판단하고 신호를 보냅니다.

2. 왜 하얀색 축구공 모양인가요? (프레넬 렌즈)

센서 위를 덮고 있는 올록볼록한 반구 모양은 단순한 덮개가 아니라 **’프레넬 렌즈(Fresnel Lens)’**입니다.

  • 주변의 적외선을 한곳으로 모아주고, 감지 범위를 넓혀주는 역할을 합니다.
  • 이 렌즈 덕분에 아주 작은 센서 하나로도 수 미터 앞의 움직임을 포착할 수 있습니다.

3. 주요 조절 장치 (뒷면 가변 저항)

보통 센서 뒷면에는 작은 나사(가변 저항)가 두 개 있습니다.

  • Sensitivity (감도): 감지 거리를 조절합니다 (약 3m ~ 7m).
  • Delay Time (지연 시간): 움직임이 감지된 후 얼마나 오랫동안 신호를 유지할지 결정합니다 (5초 ~ 5분).
    • OLED 프로젝트 팁: 지연 시간을 10~20초 정도로 설정하면 사람이 자리를 떠나고 잠시 후에 화면이 꺼지게 할 수 있어 자연스럽습니다.

4. OLED 수명 연장에 왜 최고인가요?

  • 초저전력: 센서 자체가 전기를 거의 안 먹어서 24시간 켜두어도 부담이 없습니다.
  • 비접촉: 버튼을 누를 필요가 없어 기기 마모가 없고 위생적입니다.
  • 확실한 효과: “사람이 없을 땐 끄고, 있을 때만 켠다”는 로직을 통해 OLED 소자의 발광 시간을 물리적으로 줄이는 가장 효율적인 방법입니다.

5. PIR 센서는 온도 변화에 민감합니다. (주의)

  • 에어컨 실외기 근처나 햇빛이 너무 강하게 드는 곳에 OLED 장치를 두면, 사람이 없어도 화면이 제멋대로 켜질 수 있습니다.

🐍 최종 코드

Python

from machine import Pin, I2C
import ssd1306
import time
import random
import math
import array

# ==========================================
# 1. 사용자 설정 변수 (여기서 조절하세요)
# ==========================================
BRIGHTNESS = 50       # OLED 밝기 (0~255)
KEEP_ON_SEC = 20      # 감지 시 켜짐 유지 시간 (초)
SDA_PIN, SCL_PIN = 0, 1
PIR_PIN = 3
LED_PIN = 8           # ESP32-C3 내장 LED

# 2. 하드웨어 초기화
i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
oled.contrast(BRIGHTNESS) # 밝기 적용

pir = Pin(PIR_PIN, Pin.IN, Pin.PULL_DOWN)
led = Pin(LED_PIN, Pin.OUT)

# [보조 함수] 원형/타원형 그리기
def draw_oval(cx, cy, rx, ry, fill=1):
    for y in range(-ry, ry):
        for x in range(-rx, rx):
            if (x*x)/(rx*rx) + (y*y)/(ry*ry) <= 1:
                oled.pixel(int(cx+x), int(cy+y), fill)

# 3. 15가지 눈 표정 함수
def draw_eye_fx(mode, offset_x=0, scale=1.0):
    oled.fill(0)
    lx, rx = 40 + offset_x, 88 + offset_x
    y = 28 
    base_r = int(12 * scale)

    # --- 표정 로직 (생략 없이 모두 포함) ---
    if mode == "NORMAL":
        draw_oval(lx, y, base_r, base_r); draw_oval(rx, y, base_r, base_r)
    elif mode == "BLINK":
        oled.fill_rect(lx-base_r, y, base_r*2, 2, 1); oled.fill_rect(rx-base_r, y, base_r*2, 2, 1)
    elif mode == "HAPPY":
        for i in range(-base_r, base_r):
            h = int(math.sqrt(max(0, base_r**2 - i**2)))
            oled.line(int(lx+i), y, int(lx+i), y-h, 1); oled.line(int(rx+i), y, int(rx+i), y-h, 1)
    elif mode == "ANGRY":
        l_pts = array.array('h', [int(lx-15), y-10, int(lx+15), y-5, int(lx+15), y+10, int(lx-15), y+10])
        r_pts = array.array('h', [int(rx-15), y-5, int(rx+15), y-10, int(rx+15), y+10, int(rx-15), y+10])
        oled.poly(0, 0, l_pts, 1, True); oled.poly(0, 0, r_pts, 1, True)
    elif mode == "SURPRISED":
        draw_oval(lx, y, 8, 20); draw_oval(rx, y, 8, 20)
    elif mode == "SAD":
        for i in range(-base_r, base_r):
            h = int(math.sqrt(max(0, base_r**2 - i**2)))
            oled.line(int(lx+i), y, int(lx+i), y+h, 1); oled.line(int(rx+i), y, int(rx+i), y+h, 1)
    elif mode == "SUSPICIOUS":
        draw_oval(lx, y, 20, 6); draw_oval(rx, y, 20, 6)
    elif mode == "WINK":
        draw_oval(lx, y, base_r, base_r); oled.line(int(rx-15), y, int(rx+15), y, 1)
    elif mode == "SLEEPY":
        draw_oval(lx, y+5, base_r, 6); draw_oval(rx, y+5, base_r, 6)
    elif mode == "MONEY":
        oled.text("$", int(lx-4), y-4); oled.text("$", int(rx-4), y-4)
        oled.rect(int(lx-12), y-12, 24, 24, 1); oled.rect(int(rx-12), y-12, 24, 24, 1)
    elif mode == "PAIN":
        oled.line(int(lx-10), y-10, int(lx+10), y+10, 1); oled.line(int(lx+10), y-10, int(lx-10), y+10, 1)
        oled.line(int(rx-10), y-10, int(rx+10), y+10, 1); oled.line(int(rx+10), y-10, int(rx-10), y+10, 1)
    elif mode == "LOVE":
        oled.line(int(lx-10), y-5, int(lx), y+10, 1); oled.line(int(lx), y+10, int(lx+10), y-5, 1)
        oled.line(int(rx-10), y-5, int(rx), y+10, 1); oled.line(int(rx), y+10, int(rx+10), y-5, 1)
    elif mode == "THINK":
        draw_oval(lx, y, 12, 12); draw_oval(rx, y, 5, 5)
    elif mode == "READING":
        draw_oval(lx, y, 10, 4); draw_oval(rx, y, 10, 4)
    elif mode == "DIZZY":
        oled.rect(int(lx-10), y-10, 20, 20, 1); oled.rect(int(lx-5), y-5, 10, 10, 1)
        oled.rect(int(rx-10), y-10, 20, 20, 1); oled.rect(int(rx-5), y-5, 10, 10, 1)

    text_x = (128 - (len(mode) * 8)) // 2
    oled.text(mode, text_x, 55)
    oled.show()

# 4. 메인 루프
modes = ["NORMAL", "BLINK", "HAPPY", "ANGRY", "SURPRISED", "SAD", "SUSPICIOUS", 
         "WINK", "SLEEPY", "MONEY", "PAIN", "LOVE", "THINK", "READING", "DIZZY"]

print("감지 대기 중... (LED가 켜져 있으면 대기 모드입니다)")

try:
    while True:
        if pir.value() == 1:
            # [사람 감지됨] OLED가 켜지므로 LED는 끔
            led.value(0) 
            start_time = time.time()
            
            while time.time() - start_time < KEEP_ON_SEC:
                m = random.choice(modes)
                off = random.randint(-5, 5) # 눈동자 미세 움직임
                sc = random.uniform(0.9, 1.1)
                draw_eye_fx(m, offset_x=off, scale=sc)
                
                time.sleep(random.uniform(1.0, 4.0))
                if pir.value() == 1: start_time = time.time() # 재감지 시 1분 리셋
            
            print("1분 경과, 절전 모드 진입")
            
        else:
            # [사람 없음] OLED가 꺼지므로 LED를 켬
            led.value(1) 
            oled.fill(0)
            oled.show()
            time.sleep(0.5)

except KeyboardInterrupt:
    led.value(0) # 종료 시 LED 끄기
    oled.fill(0)
    oled.show()

🐍 OLED 전면 점등 테스트 코드 (ESP32-C3)

Python

from machine import Pin, I2C
import ssd1306
import time

# 1. 하드웨어 설정 (기존 핀 설정 유지)
SDA_PIN, SCL_PIN = 0, 1
i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)

# 2. 밝기 설정 (요청하신 50)
oled.contrast(50)

# 3. 전체 화면 켜기
# fill(1)은 모든 픽셀을 흰색(점등)으로 채웁니다.
oled.fill(1)
oled.show()

print("OLED 전면 점등 테스트 중...")
print("화면에 얼룩이나 줄이 가는지 확인해 보세요.")

# 테스트 중에는 내장 LED를 꺼두겠습니다.
led = Pin(8, Pin.OUT)
led.value(0)

try:
    # 30초 동안 켜둔 후 종료
    time.sleep(30)
except KeyboardInterrupt:
    pass

# 종료 시 화면 끄기
oled.fill(0)
oled.show()
print("테스트 종료")

코멘트

“왜 내 OLED는 얼룩덜룩해질까? 수명을 2배 늘리는 팁 (feat. PIR sensor)” 에 하나의 답글

  1. micro2iot 아바타

    [구매정보] 블로그에서 사용한 부품 구매정보입니다.
    아래 링크를 통해 구매 시 소정의 수수료를 제공받으며, 채널 운영에 큰 도움이 됩니다.

micro2iot에 답글 남기기 응답 취소

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다