OLED 디스플레이 - 화면에 표시하기¶
🎯 이 장에서 배우는 것¶
- [ ] I2C 통신의 기본 원리를 설명할 수 있다
- [ ] OLED에 텍스트를 표시할 수 있다
- [ ] 센서 데이터를 OLED에 시각화할 수 있다
- [ ] 간단한 도형(선, 사각형)을 그릴 수 있다
💡 왜 이걸 배우나요?¶
지금까지 우리는 print()로 컴퓨터 화면에만 결과를 출력했어. 그런데 생각해봐 - 피코를 컴퓨터에서 분리해서 쓰고 싶다면? 예를 들어 침대 옆에 온도계를 놓고 싶다면, 컴퓨터 없이도 값을 볼 수 있어야 하잖아!
OLED 디스플레이는 바로 이 문제를 해결해줘. 작은 화면이지만 글자, 숫자, 심지어 그림까지 표시할 수 있어서 피코만으로 완전한 장치를 만들 수 있게 돼.
실제로 이런 곳에서 쓰여: - 🌡️ 스마트 온습도계 - ⌚ DIY 스마트워치 - 🎮 레트로 게임기 - 📊 서버 모니터링 장치
📚 핵심 개념¶
개념 1: I2C 통신이란?¶
- 비유로 시작: "I2C는 마치 버스 정류장 시스템과 같아요"
- 하나의 버스 노선(2개의 선)에 여러 승객(장치)이 탑승해
- 각 승객은 고유한 주소(좌석 번호)를 가지고 있어
-
버스 기사(피코)가 "42번 승객!"하고 부르면 해당 승객만 대답해
-
정확한 정의: "I2C(Inter-Integrated Circuit)는 단 2개의 선으로 여러 장치와 통신하는 프로토콜이야"
- SDA (Serial Data): 실제 데이터가 오가는 선
-
SCL (Serial Clock): 박자를 맞춰주는 신호 선
-
예시로 확인: "OLED의 기본 주소는 보통 0x3C(60번)야. 피코가 '0x3C야, 이 글자 보여줘!'하면 OLED가 화면에 표시해"
쉽게 말하면: I2C는 2개의 선만으로 여러 장치와 대화할 수 있는 효율적인 통신 방법이야!
개념 2: OLED 디스플레이¶
- 비유로 시작: "OLED는 마치 128×64칸의 모눈종이와 같아요"
- 각 칸에 점을 찍거나 지울 수 있어
-
점들을 모아서 글자나 그림을 만들어
-
정확한 정의: "0.96인치 OLED는 128×64 픽셀의 자체발광 디스플레이야"
- 백라이트 없이 픽셀 자체가 빛을 내서 선명해
-
검은 배경에 흰색(또는 노란색+파란색) 표시
-
예시로 확인: "좌표 (0,0)은 왼쪽 위 모서리, (127,63)은 오른쪽 아래 모서리야"
쉽게 말하면: OLED는 128×64개의 작은 전구가 모여있는 화면이야!
개념 3: 프레임버퍼¶
- 비유로 시작: "프레임버퍼는 마치 스케치북과 같아요"
- 스케치북에 먼저 그림을 그리고 (버퍼에 저장)
-
완성되면 액자에 걸어 (show()로 화면에 표시)
-
정확한 정의: "프레임버퍼는 화면에 표시할 내용을 메모리에 미리 그려두는 공간이야"
-
예시로 확인:
oled.text("Hello", 0, 0) # 버퍼에 그리기 oled.show() # 화면에 표시!
쉽게 말하면: 그림을 다 그린 다음에 한 번에 보여주는 거야!
🔨 따라하기¶
Step 0: 라이브러리 설치하기¶
목표: OLED를 제어하기 위한 ssd1306 라이브러리 설치
OLED를 사용하려면 먼저 라이브러리를 설치해야 해. Thonny에서 쉽게 할 수 있어!
설치 방법:
1. Thonny 상단 메뉴에서 도구(Tools) → 패키지 관리(Manage packages) 클릭
2. 검색창에 ssd1306 입력
3. micropython-ssd1306 선택 후 Install 클릭
4. 설치 완료될 때까지 대기
Step 1: OLED 연결하기¶
목표: Grove OLED를 피코에 연결하고 초기화
회로 연결: Grove Shield의 I2C 포트에 OLED 연결
코드:
# === WHAT: OLED 초기화하고 Hello 표시하기 ===
# 첫 번째 OLED 프로그램! 화면에 글자를 띄워보자
# --- WHY: 왜 필요한지 ---
# OLED를 사용하려면 먼저 I2C 통신을 설정하고
# 라이브러리로 OLED 객체를 만들어야 해
# HOW: 어떻게 동작하는지
from machine import Pin, I2C # I2C 통신을 위한 모듈
import ssd1306 # OLED 제어 라이브러리
# I2C 설정 - GP4(SDA), GP5(SCL)은 Grove Shield의 I2C 포트
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
# OLED 객체 생성 - 128x64 크기, I2C 통신 사용
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
# 화면 지우기 (모두 검은색으로)
oled.fill(0)
# 텍스트 쓰기 - ("내용", x좌표, y좌표)
oled.text("Hello!", 0, 0)
oled.text("Pico 2W", 0, 16)
# 버퍼 내용을 실제 화면에 표시
oled.show()
print("OLED 초기화 완료!")
실행 결과:
OLED 초기화 완료!
여기서 잠깐! 🤔
- freq=400000은 I2C 통신 속도야 (400kHz = 빠른 모드)
- oled.fill(0)은 검은색(0), oled.fill(1)은 흰색(1)
- 반드시 show()를 호출해야 화면에 나타나!
Step 2: 다양한 텍스트 표시하기¶
목표: 여러 줄의 텍스트를 다양한 위치에 표시
코드:
# === WHAT: 여러 텍스트를 다양한 위치에 표시 ===
# 좌표를 이해하고 원하는 위치에 글자를 배치해보자
# --- WHY: 왜 필요한지 ---
# 센서 값, 시간 등 여러 정보를 화면에 정리해서 보여주려면
# 텍스트 위치 조절을 할 줄 알아야 해
from machine import Pin, I2C
import ssd1306
# I2C, OLED 초기화
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
# 화면 지우기
oled.fill(0)
# 다양한 위치에 텍스트 배치
# 기본 글꼴은 8x8 픽셀이라 한 글자가 8픽셀 차지해
oled.text("Line 1", 0, 0) # 첫 번째 줄 (y=0)
oled.text("Line 2", 0, 10) # 두 번째 줄 (y=10)
oled.text("Line 3", 0, 20) # 세 번째 줄 (y=20)
# 오른쪽 정렬 효과 - x 좌표 조절
oled.text("Right!", 80, 40) # 오른쪽으로 이동
# 중앙 배치 (대략적으로)
oled.text("Center", 40, 56) # 아래쪽 중앙
oled.show()
print("텍스트 표시 완료!")
실행 결과: OLED에 5개의 텍스트가 각기 다른 위치에 표시돼!
여기서 잠깐! 🤔 - 기본 글꼴은 8x8 픽셀 크기야 - 한 화면에 최대 16글자 × 8줄 정도 들어가 - 한글은 지원 안 돼 😢 (영문, 숫자, 특수문자만)
Step 3: 도형 그리기¶
목표: 선, 사각형 등 기본 도형 그리기
코드:
# === WHAT: OLED에 도형 그리기 ===
# 점, 선, 사각형으로 간단한 그래픽 만들기
# --- WHY: 왜 필요한지 ---
# 그래프, 게이지, UI 요소 등을 만들려면
# 기본 도형을 그릴 줄 알아야 해
from machine import Pin, I2C
import ssd1306
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
oled.fill(0) # 화면 지우기
# === 점 찍기 ===
# pixel(x, y, color) - color: 1=흰색, 0=검은색
oled.pixel(10, 10, 1)
oled.pixel(12, 10, 1)
oled.pixel(14, 10, 1)
# === 수평선 그리기 ===
# hline(x, y, width, color)
oled.hline(0, 20, 128, 1) # 화면 전체 가로선
# === 수직선 그리기 ===
# vline(x, y, height, color)
oled.vline(64, 25, 30, 1) # 세로선
# === 사각형 (테두리만) ===
# rect(x, y, width, height, color)
oled.rect(10, 30, 40, 25, 1)
# === 채워진 사각형 ===
# fill_rect(x, y, width, height, color)
oled.fill_rect(70, 30, 40, 25, 1)
# 도형 위에 텍스트
oled.text("Shapes!", 35, 0)
oled.show()
print("도형 그리기 완료!")
실행 결과:
도형 그리기 완료!
Step 4: 센서 데이터 표시하기¶
목표: 온습도 센서 값을 OLED에 실시간 표시
코드:
# === WHAT: 센서 값을 OLED에 표시하는 대시보드 ===
# 온습도 센서(DHT11)와 OLED를 연결해서 미니 모니터 만들기
# --- WHY: 왜 필요한지 ---
# 컴퓨터 없이도 센서 값을 확인할 수 있는
# 독립적인 모니터링 장치를 만들 수 있어
from machine import Pin, I2C
import ssd1306
import dht
import time
# === 장치 초기화 ===
# I2C, OLED 설정
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
# DHT11 센서 (D16 포트)
sensor = dht.DHT11(Pin(16))
# === 대시보드 그리기 함수 ===
def draw_dashboard(temp, humi):
"""온습도 데이터를 화면에 표시"""
oled.fill(0) # 화면 지우기
# 제목
oled.text("== Weather ==", 15, 0)
# 구분선
oled.hline(0, 12, 128, 1)
# 온도 표시
oled.text("Temp:", 0, 20)
oled.text(str(temp) + " C", 60, 20)
# 습도 표시
oled.text("Humi:", 0, 35)
oled.text(str(humi) + " %", 60, 35)
# 상태 바 (습도를 막대 그래프로)
bar_width = int(humi * 1.2) # 최대 120픽셀
oled.rect(4, 50, 120, 10, 1) # 테두리
oled.fill_rect(4, 50, bar_width, 10, 1) # 채우기
oled.show()
# === 메인 루프 ===
print("센서 대시보드 시작!")
print("Ctrl+C로 종료")
while True:
try:
# 센서 측정
sensor.measure()
temp = sensor.temperature()
humi = sensor.humidity()
# 화면 업데이트
draw_dashboard(temp, humi)
# 콘솔에도 출력
print(f"온도: {temp}°C, 습도: {humi}%")
except Exception as e:
# 센서 오류 시
oled.fill(0)
oled.text("Sensor Error!", 10, 28)
oled.show()
print(f"오류: {e}")
time.sleep(2) # 2초마다 갱신
실행 결과:
센서 대시보드 시작!
Ctrl+C로 종료
온도: 24°C, 습도: 65%
온도: 24°C, 습도: 64%
...
여기서 잠깐! 🤔
- fill(0)으로 매번 화면을 지우고 다시 그려
- 안 그러면 이전 글자 위에 새 글자가 겹쳐!
📝 전체 코드¶
# === 센서 데이터 OLED 대시보드 완성판 ===
# 파일명: step4_oled_dashboard.py
# 온습도 센서 값을 OLED에 실시간으로 표시하는 프로그램
from machine import Pin, I2C
import ssd1306
import dht
import time
# ===== 장치 초기화 =====
# I2C 통신 설정 (Grove Shield의 I2C 포트)
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
# OLED 디스플레이 (128x64 픽셀)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
# DHT11 온습도 센서 (D16 포트에 연결)
sensor = dht.DHT11(Pin(16))
# ===== 함수 정의 =====
def draw_dashboard(temp, humi):
"""온습도 데이터를 대시보드 형태로 표시
Args:
temp: 온도 값 (정수)
humi: 습도 값 (정수)
"""
oled.fill(0) # 화면 초기화 (검은색)
# 제목 영역
oled.text("== Weather ==", 15, 0)
oled.hline(0, 12, 128, 1) # 구분선
# 온도 표시
oled.text("Temp:", 0, 20)
oled.text(str(temp) + " C", 60, 20)
# 습도 표시
oled.text("Humi:", 0, 35)
oled.text(str(humi) + " %", 60, 35)
# 습도 막대 그래프
oled.rect(4, 50, 120, 10, 1) # 테두리
bar_width = int(humi * 1.2) # 습도에 비례한 막대 길이
if bar_width > 0:
oled.fill_rect(4, 50, bar_width, 10, 1)
oled.show() # 화면에 표시
def show_error(message):
"""에러 메시지를 화면에 표시
Args:
message: 표시할 에러 메시지
"""
oled.fill(0)
oled.text("ERROR!", 40, 20)
oled.text(message, 0, 35)
oled.show()
# ===== 메인 프로그램 =====
print("=" * 30)
print("센서 대시보드 시작!")
print("2초마다 센서 값을 갱신합니다")
print("Ctrl+C로 종료하세요")
print("=" * 30)
# 시작 화면
oled.fill(0)
oled.text("Starting...", 25, 28)
oled.show()
time.sleep(1)
# 무한 루프
while True:
try:
# 센서에서 데이터 읽기
sensor.measure()
temp = sensor.temperature()
humi = sensor.humidity()
# OLED에 표시
draw_dashboard(temp, humi)
# 콘솔에도 출력
print(f"온도: {temp}°C | 습도: {humi}%")
except OSError as e:
# 센서 통신 오류
show_error("Sensor N/A")
print(f"센서 오류: {e}")
except Exception as e:
# 기타 오류
show_error("Unknown Err")
print(f"오류 발생: {e}")
# 2초 대기
time.sleep(2)
⚠️ 자주 하는 실수¶
실수 1: show()를 안 부름¶
증상: 코드는 에러 없이 실행되는데 화면에 아무것도 안 나와
원인: OLED는 프레임버퍼 방식이라 show()를 호출해야 실제 화면이 갱신돼
해결:
# 잘못된 코드
oled.text("Hello", 0, 0)
# 여기서 끝나면 화면에 안 나타남!
# 올바른 코드
oled.text("Hello", 0, 0)
oled.show() # 이걸 꼭 호출해야 해!
실수 2: 화면을 안 지우고 덮어씀¶
증상: 글자가 겹쳐서 뭉개져 보임
원인: 이전에 그린 내용 위에 새 내용이 덧그려짐
해결:
# 잘못된 코드
while True:
oled.text(str(count), 0, 0) # 계속 겹침
oled.show()
count += 1
# 올바른 코드
while True:
oled.fill(0) # 먼저 화면 지우기!
oled.text(str(count), 0, 0)
oled.show()
count += 1
실수 3: I2C 핀 번호 오류¶
증상: OSError: [Errno 5] EIO 에러 발생
원인: I2C 핀 번호가 잘못됐거나 연결이 안 됨
해결:
# 잘못된 코드 (핀 번호 틀림)
i2c = I2C(0, sda=Pin(0), scl=Pin(1))
# 올바른 코드 (Grove Shield I2C 포트)
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
실수 4: 좌표가 화면을 벗어남¶
증상: 글자나 도형이 잘려서 보이거나 안 보임
원인: x는 0~127, y는 0~63 범위를 벗어남
해결:
# 잘못된 코드
oled.text("Hello", 100, 0) # 글자가 오른쪽으로 잘림
oled.text("Test", 0, 60) # 글자가 아래로 잘림
# 올바른 코드 (글자 크기 8x8 고려)
oled.text("Hello", 88, 0) # 5글자 * 8 = 40, 128-40 = 88
oled.text("Test", 0, 56) # 64-8 = 56
실수 5: 라이브러리 설치 안 함¶
증상: ImportError: no module named 'ssd1306'
원인: ssd1306 라이브러리가 피코에 설치되지 않음
해결:
1. Thonny → 도구 → 패키지 관리
2. ssd1306 검색
3. micropython-ssd1306 설치
✅ 스스로 점검하기¶
-
I2C 통신에서 SDA와 SCL의 역할은 각각 무엇인가요?
-
OLED에 "Score: 100"을 화면 중앙에 표시하려면 x 좌표를 대략 얼마로 해야 할까요?
-
왜 화면을 갱신하기 전에
oled.fill(0)을 호출하는 게 좋을까요?
정답 확인
1. **SDA**(Serial Data)는 실제 데이터가 오가는 선이고, **SCL**(Serial Clock)은 데이터 전송의 타이밍(박자)을 맞춰주는 신호 선이야. 2. "Score: 100"은 10글자야. 글자당 8픽셀이니까 총 80픽셀. 중앙 정렬: (128 - 80) / 2 = **24** 정도! 3. `fill(0)`을 안 하면 이전에 그렸던 내용이 그대로 남아있어서 새 내용과 겹쳐 보여. 화면을 깨끗이 지우고 다시 그려야 깔끔하게 표시돼!🚀 더 해보기¶
도전 1: 카운터 만들기 (쉬움)¶
버튼을 누를 때마다 숫자가 1씩 증가하고 OLED에 표시되는 카운터를 만들어봐!
힌트:
count = 0
# 버튼 누르면 count += 1
# OLED에 count 표시
도전 2: 간단한 애니메이션 (중간)¶
사각형이 왼쪽에서 오른쪽으로 이동하는 애니메이션을 만들어봐!
힌트:
for x in range(0, 100, 5):
oled.fill(0)
oled.fill_rect(x, 28, 20, 20, 1)
oled.show()
time.sleep(0.1)
도전 3: 미니 게임 만들기 ⭐ (어려움)¶
조도 센서 값에 따라 막대가 움직이는 게임을 만들어봐! - 막대가 목표 영역에 들어오면 점수 획득 - 점수와 레벨을 OLED에 표시
힌트:
# 조도 값을 y 좌표로 변환
# 목표 영역과 충돌 체크
# 점수 시스템 추가
🔗 다음 장으로¶
이번 장에서 우리는: - ✅ I2C 통신으로 OLED와 대화하는 법을 배웠어 - ✅ 텍스트, 선, 사각형을 화면에 그릴 수 있게 됐어 - ✅ 센서 데이터를 실시간으로 시각화하는 대시보드를 만들었어
다음 장에서는 피코를 인터넷에 연결해볼 거야! WiFi로 세상과 연결되면 날씨 정보도 받아오고, 시간도 동기화할 수 있어. 지금 만든 OLED 대시보드에 인터넷 정보까지 더하면 진짜 IoT 장치가 완성돼! 🌐