콘텐츠로 이동

Chapter 6: 조건과 반복 - 자동화의 시작


🎯 이 장에서 배우는 것

  • [ ] if/elif/else를 활용한 다중 조건을 구현할 수 있다
  • [ ] while 무한루프로 지속적인 모니터링을 할 수 있다
  • [ ] 센서→조건→출력 연결로 자동 야간등을 만들 수 있다
  • [ ] break와 continue로 루프를 제어할 수 있다

💡 왜 이걸 배우나요?

지금까지 우리는 버튼을 누르면 LED가 켜지고, 센서 값을 읽어서 화면에 출력하는 것을 배웠어. 근데 생각해봐. 너희가 자고 있을 때 방이 어두워지면 자동으로 무드등이 켜진다면? 온도가 30도가 넘으면 알아서 선풍기가 돌아간다면?

이게 바로 자동화야! 🤖

우리 주변의 스마트 가전들이 전부 이 원리로 동작해: - 🚗 자동차 후방 센서: 거리가 가까우면 → 경고음 - 🏠 현관 자동 조명: 어두워지면 + 사람 감지 → 불 켜짐 - 🌡️ 에어컨: 온도가 설정값보다 높으면 → 가동

이 모든 게 "조건문 + 반복문" 조합이야. 오늘 이걸 마스터하면 너도 자동화 시스템을 만들 수 있어!


📚 핵심 개념

개념 1: if/elif/else - 여러 갈래 길에서 선택하기

비유로 시작: 자판기를 생각해봐. 동전을 넣으면 금액에 따라 살 수 있는 음료가 달라지지? 500원이면 물만, 1000원이면 주스도, 1500원이면 커피까지. 이렇게 여러 조건을 순서대로 확인하는 게 if/elif/else야.

정확한 정의: if는 첫 번째 조건을 검사하고, elif(else if의 줄임말)는 앞의 조건이 거짓일 때 다음 조건을 검사해. else는 모든 조건이 거짓일 때 실행되는 "나머지" 상황이야.

예시로 확인:

money = 1000

if money >= 1500:
    print("커피 구매 가능!")
elif money >= 1000:
    print("주스 구매 가능!")
elif money >= 500:
    print("물 구매 가능!")
else:
    print("금액이 부족해요")

쉽게 말하면: 위에서부터 조건을 하나씩 확인하다가, 처음으로 True가 나오면 그 블록만 실행하고 끝!

flowchart TD A[🪙 돈 확인] --> B{1500원 이상?} B -->|Yes| C[☕ 커피 구매] B -->|No| D{1000원 이상?} D -->|Yes| E[🧃 주스 구매] D -->|No| F{500원 이상?} F -->|Yes| G[💧 물 구매] F -->|No| H[❌ 금액 부족] style A fill:#e3f2fd,stroke:#1976d2 style B fill:#fff3e0,stroke:#f57c00 style D fill:#fff3e0,stroke:#f57c00 style F fill:#fff3e0,stroke:#f57c00 style C fill:#e8f5e9,stroke:#388e3c style E fill:#e8f5e9,stroke:#388e3c style G fill:#e8f5e9,stroke:#388e3c style H fill:#ffebee,stroke:#c62828

개념 2: while 무한루프 - 계속 감시하는 보초병

비유로 시작: 경비원을 생각해봐. 건물 입구에서 24시간 계속 "이상 없나?" 확인하잖아. while 무한루프는 바로 이 경비원 같은 거야. 계속 반복하면서 뭔가 변화가 있는지 감시해.

정확한 정의: while True:는 조건이 항상 참이므로 무한히 반복해. 피지컬 컴퓨팅에서는 이걸로 센서 값을 계속 읽어서 모니터링하는 게 기본 패턴이야.

예시로 확인:

while True:  # 무한 반복!
    sensor_value = read_sensor()  # 센서 읽기
    print(sensor_value)           # 출력
    time.sleep(1)                 # 1초 대기

쉽게 말하면: "영원히 반복해!" 그래서 피코가 꺼질 때까지 계속 실행돼.


개념 3: break와 continue - 루프 탈출 비상구

비유로 시작: 놀이공원 회전목마를 타고 있는데, 갑자기 비가 오면? break로 완전히 내리거나, continue로 "이번 바퀴는 패스!" 할 수 있어.

정확한 정의: - break: 루프를 완전히 종료하고 빠져나감 - continue: 이번 반복만 건너뛰고 다음 반복으로

예시로 확인:

count = 0
while True:
    count += 1

    if count == 3:
        continue  # 3은 건너뛰기

    if count > 5:
        break     # 5 넘으면 종료

    print(count)  # 출력: 1, 2, 4, 5

쉽게 말하면: break는 "그만!", continue는 "패스!"

flowchart LR A[🔄 루프 시작] --> B[⚙️ 코드 실행] B --> C{continue?} C -->|Yes| A C -->|No| D{break?} D -->|Yes| E[🚪 루프 종료] D -->|No| A style A fill:#e3f2fd,stroke:#1976d2 style B fill:#fff3e0,stroke:#f57c00 style C fill:#f3e5f5,stroke:#7b1fa2 style D fill:#f3e5f5,stroke:#7b1fa2 style E fill:#ffebee,stroke:#c62828

🔨 따라하기

Step 1: 다중 조건으로 밝기 레벨 판단하기

목표: 조도 센서 값에 따라 "매우 밝음/밝음/보통/어두움/매우 어두움" 5단계로 분류하기

준비물: - 라즈베리파이 피코 2W - Grove Shield - Grove 조도 센서 (A0 포트 연결)

회로 연결:

flowchart LR subgraph Pico["🔌 Pico 2W + Grove Shield"] A0["A0 포트"] end subgraph Light["💡 조도 센서"] VCC["VCC"] GND["GND"] SIG["SIG"] end A0 <-->|Grove 케이블| Light style Pico fill:#e3f2fd,stroke:#1976d2 style Light fill:#fff3e0,stroke:#f57c00

코드:

# === WHAT: 조도 센서로 밝기 레벨 판단하기 ===
# 조도 센서 값을 읽어서 5단계로 분류해 출력하는 코드

# --- WHY: 왜 필요한지 ---
# 단순히 숫자만 보면 어두운지 밝은지 감이 안 와
# 사람이 이해하기 쉬운 "레벨"로 변환하면 직관적!

# HOW: 어떻게 동작하는지
from machine import ADC, Pin  # ADC: 아날로그를 디지털로 변환
import time

# 조도 센서 설정 (A0 = GP26)
light_sensor = ADC(Pin(26))

# 밝기 레벨 기준값 (0~65535 범위에서)
VERY_BRIGHT = 50000   # 이 이상이면 매우 밝음
BRIGHT = 35000        # 이 이상이면 밝음
NORMAL = 20000        # 이 이상이면 보통
DIM = 10000           # 이 이상이면 어두움
                      # 이 미만이면 매우 어두움

# 센서 값 한 번 읽기
light_value = light_sensor.read_u16()
print(f"조도 센서 값: {light_value}")

# 다중 조건으로 레벨 판단
if light_value >= VERY_BRIGHT:
    level = "☀️ 매우 밝음"
elif light_value >= BRIGHT:
    level = "🌤️ 밝음"
elif light_value >= NORMAL:
    level = "⛅ 보통"
elif light_value >= DIM:
    level = "🌥️ 어두움"
else:
    level = "🌑 매우 어두움"

print(f"밝기 레벨: {level}")

실행 결과:

조도 센서 값: 42000
밝기 레벨: 🌤️ 밝음

여기서 잠깐! 🤔

if를 5번 쓰지 않고 elif를 썼을까?

만약 전부 if로 쓰면:

if light_value >= 50000:   # ✅ True라면
    level = "매우 밝음"
if light_value >= 35000:   # ✅ 이것도 True! (50000 > 35000이니까)
    level = "밝음"         # 덮어씌워짐!

elif는 앞의 조건이 True면 아예 검사를 안 해서 이런 문제가 없어!


Step 2: while 무한루프로 실시간 모니터링

목표: 조도 센서 값을 1초마다 계속 읽어서 출력하기

코드:

# === WHAT: 조도 센서 실시간 모니터링 ===
# 1초마다 센서 값을 읽어서 밝기 레벨을 계속 출력

# --- WHY: 왜 필요한지 ---
# 센서 값은 계속 변해! 손으로 가리면 어두워지고, 
# 전등 켜면 밝아지고. 이걸 "실시간"으로 봐야 의미있지

# HOW: 어떻게 동작하는지
from machine import ADC, Pin
import time

# 센서 설정
light_sensor = ADC(Pin(26))

# 기준값 (상수로 정의 - 대문자 관례)
VERY_BRIGHT = 50000
BRIGHT = 35000
NORMAL = 20000
DIM = 10000

def get_light_level(value):
    """밝기 값을 레벨 문자열로 변환하는 함수"""
    if value >= VERY_BRIGHT:
        return "☀️ 매우 밝음"
    elif value >= BRIGHT:
        return "🌤️ 밝음"
    elif value >= NORMAL:
        return "⛅ 보통"
    elif value >= DIM:
        return "🌥️ 어두움"
    else:
        return "🌑 매우 어두움"

# 무한 모니터링 시작!
print("=== 조도 모니터링 시작 ===")
print("(Ctrl+C 또는 Thonny 정지 버튼으로 종료)")
print()

while True:
    # 센서 값 읽기
    light_value = light_sensor.read_u16()

    # 레벨 판단
    level = get_light_level(light_value)

    # 출력 (같은 줄에 덮어쓰기)
    print(f"\r값: {light_value:5d} | 레벨: {level}    ", end="")

    # 1초 대기
    time.sleep(1)

실행 결과:

=== 조도 모니터링 시작 ===
(Ctrl+C 또는 Thonny 정지 버튼으로 종료)

값: 42000 | 레벨: 🌤️ 밝음    
(손으로 센서를 가리면 실시간으로 값이 변해!)

여기서 잠깐! 🤔

\rend=""가 뭐야? - \r: 커서를 줄 맨 앞으로 이동 (줄바꿈 없이!) - end="": print 끝에 줄바꿈 안 함

이 둘을 조합하면 같은 줄에서 값이 계속 업데이트되는 효과! 마치 디지털 시계처럼 🕐


Step 3: 자동 야간등 만들기 - 센서→조건→출력 연결!

목표: 어두워지면 자동으로 LED가 켜지는 스마트 야간등 만들기

준비물 추가: - Grove LED (D18 포트 연결)

회로 연결:

flowchart TB subgraph Pico["🔌 Pico 2W + Grove Shield"] A0["A0 포트"] D18["D18 포트"] end subgraph Sensors["📡 입력"] Light["💡 조도 센서"] end subgraph Outputs["📤 출력"] LED["🔴 LED"] end A0 <-->|Grove 케이블| Light D18 <-->|Grove 케이블| LED style Pico fill:#e3f2fd,stroke:#1976d2 style Sensors fill:#fff3e0,stroke:#f57c00 style Outputs fill:#e8f5e9,stroke:#388e3c

코드:

# === WHAT: 자동 야간등 ===
# 어두워지면 LED가 켜지고, 밝아지면 LED가 꺼지는 자동화 시스템

# --- WHY: 왜 필요한지 ---
# 매번 스위치 누르기 귀찮잖아!
# 센서가 알아서 판단해서 자동으로 켜고 끄면 편하지

# HOW: 어떻게 동작하는지
from machine import ADC, Pin
import time

# === 하드웨어 설정 ===
light_sensor = ADC(Pin(26))  # 조도 센서 (A0 = GP26)
led = Pin(18, Pin.OUT)       # LED (D18 = GP18)

# === 기준값 설정 ===
# 이 값보다 어두우면 LED 켜기!
DARK_THRESHOLD = 15000  

# 히스테리시스: 경계값에서 깜빡임 방지
# "끌 때는 좀 더 밝아야 꺼" 라는 의미
LIGHT_THRESHOLD = 20000  

# === 상태 변수 ===
led_on = False  # 현재 LED 상태 추적

# === 메인 루프 ===
print("=== 🌙 자동 야간등 시작 ===")
print(f"어두움 기준: {DARK_THRESHOLD} 이하")
print(f"밝음 기준: {LIGHT_THRESHOLD} 이상")
print()

while True:
    # 센서 값 읽기
    light_value = light_sensor.read_u16()

    # 조건 판단 및 LED 제어
    if light_value < DARK_THRESHOLD and not led_on:
        # 어둡고, LED가 꺼져있으면 → 켜기
        led.on()
        led_on = True
        print(f"💡 LED ON! (밝기: {light_value})")

    elif light_value > LIGHT_THRESHOLD and led_on:
        # 밝고, LED가 켜져있으면 → 끄기
        led.off()
        led_on = False
        print(f"🌑 LED OFF! (밝기: {light_value})")

    # 현재 상태 표시
    status = "💡 ON" if led_on else "🌑 OFF"
    print(f"\r밝기: {light_value:5d} | LED: {status}  ", end="")

    # 0.5초마다 확인
    time.sleep(0.5)

실행 결과:

=== 🌙 자동 야간등 시작 ===
어두움 기준: 15000 이하
밝음 기준: 20000 이상

밝기: 42000 | LED: 🌑 OFF  
(센서를 손으로 가리면...)
💡 LED ON! (밝기: 8500)
밝기:  8500 | LED: 💡 ON  
(손을 떼면...)
🌑 LED OFF! (밝기: 43000)
밝기: 43000 | LED: 🌑 OFF  

여기서 잠깐! 🤔

히스테리시스(Hysteresis)가 뭐야?

에어컨을 생각해봐. 25도로 설정하면: - 25도가 되면 꺼지고 - 바로 25.1도가 되면 다시 켜지고 - 또 25도가 되면 꺼지고...

이러면 계속 켜졌다 꺼졌다 깜빡이겠지? 😵

그래서 실제 에어컨은: - 24도가 되면 끄고 (여유있게!) - 26도가 되면 켜기

이 "여유 구간"이 히스테리시스야. 우리 야간등도 같은 원리!

flowchart LR subgraph Range["밝기 값 범위"] direction TB Dark["🌑 어두움<br/>< 15000"] Buffer["⚡ 완충 구간<br/>15000 ~ 20000"] Bright["☀️ 밝음<br/>> 20000"] end Dark -->|"LED 켜짐 유지"| Buffer Buffer -->|"상태 유지<br/>(변화 없음)"| Buffer Bright -->|"LED 꺼짐 유지"| Buffer style Dark fill:#1a237e,stroke:#0d47a1,color:#fff style Buffer fill:#fff3e0,stroke:#f57c00 style Bright fill:#fff9c4,stroke:#f9a825

📝 전체 코드

# === 자동 야간등 완성 버전 ===
# 조도 센서로 밝기를 감지해서 자동으로 LED를 켜고 끄는 시스템
# 히스테리시스 적용으로 안정적인 동작 보장

from machine import ADC, Pin
import time

# ========================================
# 하드웨어 설정
# ========================================
light_sensor = ADC(Pin(26))  # 조도 센서 (A0 포트 = GP26)
led = Pin(18, Pin.OUT)       # LED (D18 포트 = GP18)

# ========================================
# 설정값 (환경에 맞게 조절하세요!)
# ========================================
DARK_THRESHOLD = 15000   # 이 값 이하면 "어둡다"
LIGHT_THRESHOLD = 20000  # 이 값 이상이면 "밝다"
CHECK_INTERVAL = 0.5     # 확인 주기 (초)

# ========================================
# 밝기 레벨 판단 함수
# ========================================
def get_brightness_level(value):
    """밝기 값을 5단계 레벨로 변환"""
    if value >= 50000:
        return "☀️ 매우 밝음", 5
    elif value >= 35000:
        return "🌤️ 밝음", 4
    elif value >= 20000:
        return "⛅ 보통", 3
    elif value >= 10000:
        return "🌥️ 어두움", 2
    else:
        return "🌑 매우 어두움", 1

# ========================================
# 메인 프로그램
# ========================================
def main():
    """자동 야간등 메인 함수"""
    led_on = False  # LED 상태 추적

    print("=" * 40)
    print("🌙 자동 야간등 시스템")
    print("=" * 40)
    print(f"어두움 기준: {DARK_THRESHOLD} 이하 → LED ON")
    print(f"밝음 기준: {LIGHT_THRESHOLD} 이상 → LED OFF")
    print(f"확인 주기: {CHECK_INTERVAL}초")
    print("-" * 40)
    print("Ctrl+C 또는 정지 버튼으로 종료")
    print("=" * 40)
    print()

    while True:
        # 1. 센서 값 읽기
        light_value = light_sensor.read_u16()

        # 2. 레벨 판단
        level_text, level_num = get_brightness_level(light_value)

        # 3. LED 제어 (히스테리시스 적용)
        if light_value < DARK_THRESHOLD and not led_on:
            led.on()
            led_on = True
            print(f"\n💡 LED 켜짐! (밝기: {light_value})")

        elif light_value > LIGHT_THRESHOLD and led_on:
            led.off()
            led_on = False
            print(f"\n🔌 LED 꺼짐! (밝기: {light_value})")

        # 4. 상태 출력
        led_status = "💡 ON " if led_on else "⚫ OFF"
        bar = "█" * level_num + "░" * (5 - level_num)  # 그래픽 바
        print(f"\r[{bar}] {light_value:5d} {level_text:12s} | LED: {led_status}", end="")

        # 5. 대기
        time.sleep(CHECK_INTERVAL)

# 프로그램 시작
if __name__ == "__main__":
    main()

⚠️ 자주 하는 실수

실수 1: elif 대신 if를 연속으로 사용

증상: 조건이 여러 개 맞아서 원하지 않는 결과가 나옴

원인: if는 독립적으로 각각 검사하지만, elif는 앞 조건이 거짓일 때만 검사함

해결:

# ❌ 잘못된 코드
score = 85
if score >= 60:
    grade = "D"
if score >= 70:
    grade = "C"
if score >= 80:
    grade = "B"  # 85점인데 B만 출력됨? 
if score >= 90:
    grade = "A"
# 결과: grade = "B" (맞긴 한데... 위험한 코드!)

# ✅ 올바른 코드
score = 85
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"  # 여기서 멈춤!
elif score >= 70:
    grade = "C"
elif score >= 60:
    grade = "D"
else:
    grade = "F"
# 결과: grade = "B" (안전하고 명확!)


실수 2: while 루프에서 time.sleep() 빼먹기

증상: 피코가 멈추거나 Thonny가 응답 없음

원인: CPU가 쉬지 않고 100% 풀가동하면서 다른 작업(통신 등)을 못함

해결:

# ❌ 잘못된 코드
while True:
    value = sensor.read_u16()
    print(value)
    # sleep 없음 → CPU 폭주!

# ✅ 올바른 코드
while True:
    value = sensor.read_u16()
    print(value)
    time.sleep(0.1)  # 최소 0.1초라도!


실수 3: 조건문에서 = 와 == 혼동

증상: SyntaxError 또는 예상치 못한 동작

원인: =는 값 할당, ==는 비교 연산자

해결:

# ❌ 잘못된 코드
if light_value = 1000:  # SyntaxError!
    print("어둡다")

# ✅ 올바른 코드
if light_value == 1000:  # 같은지 비교
    print("정확히 1000이다")

if light_value < 1000:   # 작은지 비교
    print("어둡다")


실수 4: 무한루프 탈출 못하기

증상: 프로그램이 영원히 안 끝남, Thonny 정지 버튼도 안 먹힘

원인: 무한루프 안에 탈출 조건이 없음

해결:

# ❌ 위험한 코드 (탈출구 없음)
while True:
    do_something()

# ✅ 안전한 코드 (버튼으로 종료 가능)
from machine import Pin
button = Pin(20, Pin.IN, Pin.PULL_DOWN)

while True:
    if button.value() == 1:  # 버튼 누르면
        print("종료!")
        break  # 탈출!
    do_something()
    time.sleep(0.1)


✅ 스스로 점검하기

1. 사실 확인 질문

다음 코드의 출력 결과는?

x = 75
if x >= 90:
    print("A")
elif x >= 80:
    print("B")
elif x >= 70:
    print("C")
else:
    print("D")

2. 적용 질문

온도가 28도 이상이면 "덥다", 18도 이하면 "춥다", 그 사이면 "쾌적하다"를 출력하는 코드를 작성해봐.

3. 이유 질문

자동 야간등에서 DARK_THRESHOLDLIGHT_THRESHOLD를 같은 값(예: 둘 다 15000)으로 설정하면 어떤 문제가 생길까?

정답 확인 **1번 답**: `C` - 75는 90 이상? ❌ - 75는 80 이상? ❌ - 75는 70 이상? ✅ → "C" 출력! **2번 답**:
temp = 25  # 예시 온도

if temp >= 28:
    print("덥다")
elif temp <= 18:
    print("춥다")
else:
    print("쾌적하다")
**3번 답**: 경계값 근처에서 LED가 빠르게 켜졌다 꺼졌다 깜빡이는 현상이 생겨! 예를 들어 값이 14900 → 15100 → 14900 이렇게 조금씩 흔들리면: - 14900: 어둡다 → LED ON - 15100: 밝다 → LED OFF - 14900: 어둡다 → LED ON - (반복) 그래서 히스테리시스로 "켤 때 기준"과 "끌 때 기준"을 다르게 설정하는 거야!

🚀 더 해보기

도전 1: 3단계 야간등 (쉬움) ⭐

어두움 정도에 따라 LED 밝기를 3단계로 조절해봐! - 매우 어두움 → LED 최대 밝기 - 어두움 → LED 중간 밝기
- 밝음 → LED 끄기

힌트: PWM으로 LED 밝기 조절 (5차시 복습!)

from machine import PWM
led_pwm = PWM(Pin(18))
led_pwm.freq(1000)
led_pwm.duty_u16(32768)  # 50% 밝기


도전 2: 버튼으로 모드 전환 (중간) ⭐⭐

버튼을 누를 때마다 모드가 바뀌는 야간등을 만들어봐! - 모드 1: 자동 모드 (센서로 제어) - 모드 2: 항상 켜짐 - 모드 3: 항상 꺼짐

힌트: 모드 변수와 버튼 토글

mode = 1  # 1, 2, 3 순환

if button.value() == 1:
    mode = mode + 1
    if mode > 3:
        mode = 1
    time.sleep(0.3)  # 디바운싱


도전 3: 움직임 감지 야간등 (어려움) ⭐⭐⭐

조도 센서 + 초음파 센서를 조합한 스마트 야간등! - 어두울 때만 활성화 - 사람이 가까이 오면(50cm 이내) LED 켜짐 - 10초 동안 움직임 없으면 자동으로 꺼짐

힌트: 시간 추적 변수 사용

import time
last_motion_time = time.ticks_ms()  # 마지막 움직임 시간

# 움직임 감지되면
last_motion_time = time.ticks_ms()  # 시간 갱신

# 10초 지났는지 확인
if time.ticks_diff(time.ticks_ms(), last_motion_time) > 10000:
    # 10초 지남!


🔗 다음 장으로

이번 장에서 우리는: - ✅ if/elif/else로 여러 조건을 순차적으로 검사하는 법 - ✅ while True로 센서를 계속 모니터링하는 법
- ✅ 센서→조건→출력을 연결해서 자동화 시스템 만드는 법 - ✅ 히스테리시스로 안정적인 동작 보장하는 법

이걸 배웠어!

지금까지의 코드를 보면 비슷한 패턴이 계속 반복되지 않아? 센서 읽기, LED 제어, 상태 출력...

다음 장 "함수로 정리"에서는 이런 반복되는 코드를 함수로 깔끔하게 정리하는 법을 배울 거야. 마치 레고 블록처럼 조립해서 쓸 수 있게! 🧱

코드가 길어져도 걱정 없이 관리할 수 있는 비결을 알려줄게! 🚀