콘텐츠 대표 이미지 - IoT와 라즈베리파이를 활용한 Python 하드웨어 제어 완전 정복 가이드
🐍 Python × IoT

IoT와 라즈베리파이를 활용한 Python 하드웨어 제어 완전 정복 가이드

현실 세계를 코드로 움직이는 법 — 센서부터 액추에이터까지, 진짜 개발자처럼 다뤄보자 🚀

야 솔직히 말해서, 코드 짜다가 화면 속에서만 뭔가 움직이는 거 보면 좀 심심하지 않음? 🤔
근데 라즈베리파이에 Python 코드 한 줄 넣었더니 실제 LED가 반짝이고, 온도 센서가 값을 뱉고, 모터가 돌아가는 거 보면... 진짜 소름 돋음 ㄹㅇ 🔥
오늘은 IoT(사물인터넷)와 라즈베리파이를 활용해서 Python으로 하드웨어를 제어하는 방법을 처음부터 끝까지 싹 다 파헤쳐볼 거야.
초보자도 이해할 수 있게, 근데 내용은 진짜 알차게 — 시작해보자고!


🌐 1. IoT가 뭔데? 라즈베리파이는 또 뭐고?

IoT(Internet of Things, 사물인터넷)는 말 그대로 "사물들이 인터넷에 연결된다"는 개념이야.
냉장고가 인터넷에 연결되고, 전구가 스마트폰으로 제어되고, 공장 기계가 실시간으로 데이터를 보내는 것 — 이게 다 IoT임.
2024년 기준으로 전 세계 IoT 기기 수는 약 170억 개를 넘어섰고, 2030년에는 300억 개를 돌파할 거라는 전망이 있어. 진짜 어마어마하지? 😲

IoT 허브 🌡️ 센서 온도/습도 💡 LED 조명제어 ⚙️ 모터 액추에이터 📡 통신 WiFi/MQTT 📱 앱 원격제어 ☁️ 클라우드 데이터저장 IoT 생태계 구조도

🍓 라즈베리파이(Raspberry Pi)란?

라즈베리파이는 영국의 Raspberry Pi Foundation에서 만든 초소형 싱글보드 컴퓨터(SBC)야.
신용카드 크기만 한 이 작은 보드에 CPU, RAM, GPU, USB, HDMI, GPIO 핀이 다 들어있어.
가격은 모델에 따라 다르지만 Raspberry Pi 4 Model B 기준으로 약 35~75달러 수준이고,
2024년에 출시된 Raspberry Pi 5는 성능이 대폭 향상되어 더 복잡한 IoT 프로젝트도 거뜬히 처리해.

모델CPURAM특징
Pi Zero 2 W1GHz 쿼드코어512MB초소형, 저전력, WiFi 내장
Pi 3 Model B+1.4GHz 쿼드코어1GBWiFi/BT 내장, 안정적
Pi 4 Model B1.8GHz 쿼드코어1~8GBUSB3.0, 듀얼 4K 지원
Pi 52.4GHz 쿼드코어4~8GBPCIe 지원, 대폭 성능향상
💡 왜 라즈베리파이에서 Python을 쓰냐고? 라즈베리파이의 공식 OS인 Raspberry Pi OS(구 Raspbian)에는 Python이 기본으로 설치되어 있어.
게다가 GPIO 제어, 센서 통신, 네트워크 연결 등을 위한 Python 라이브러리가 엄청 풍부하게 있거든.
진입장벽이 낮고 생태계가 넓어서 IoT 입문에 최적의 조합이야! 🎯

🔧 2. 개발 환경 세팅 — 이것부터 해야 진짜 시작

📦 필요한 준비물

하드웨어 제어 시작 전에 뭐가 필요한지 체크해보자:

🖥️ 하드웨어 준비물
  • ✅ 라즈베리파이 (Pi 3B+ 이상 권장)
  • ✅ MicroSD 카드 (16GB 이상, Class 10 권장)
  • ✅ 5V/3A 이상 USB-C 전원 어댑터
  • ✅ 브레드보드 (빵판)
  • ✅ 점퍼 와이어 (암-수, 수-수 세트)
  • ✅ LED, 저항(220Ω, 10kΩ), 버튼 등 기본 부품
  • ✅ 각종 센서 (DHT11, HC-SR04 등)

💿 OS 설치 및 초기 설정

1
Raspberry Pi Imager 다운로드
공식 사이트(raspberrypi.com)에서 Raspberry Pi Imager를 PC에 설치해.
이걸로 MicroSD에 OS를 굽는 거야. 2024년 기준 최신 OS는 Raspberry Pi OS (Bookworm)이야.
2
OS 이미지 선택 및 굽기
Imager 실행 → OS 선택 → "Raspberry Pi OS (64-bit)" 선택 → MicroSD 선택 → Write
⚠️ 굽기 전에 고급 설정(⚙️)에서 WiFi, SSH, 사용자명/비번 미리 설정하면 편해!
3
Python 환경 확인
라즈베리파이 부팅 후 터미널에서 Python 버전 확인: Terminal
python3 --version
# Python 3.11.x 이상이면 OK!
4
필수 라이브러리 설치
Terminal
sudo apt update && sudo apt upgrade -y
sudo apt install python3-pip python3-venv -y
pip3 install RPi.GPIO gpiozero adafruit-blinka
⚠️ 주의! 라즈베리파이 5부터는 RPi.GPIO 대신 lgpio 또는 gpiozero 사용을 권장해.
RPi.GPIO는 Pi 5의 새로운 GPIO 칩(RP1)과 호환 이슈가 있거든. gpiozero는 두 버전 모두 지원하니까 gpiozero 쓰는 게 미래지향적이야!

🔌 GPIO 핀 이해하기

GPIO(General Purpose Input/Output)는 라즈베리파이의 핵심 인터페이스야.
라즈베리파이 3B+ 이상 모델은 40핀 GPIO 헤더를 가지고 있어.

GPIO 핀 헤더 (40핀) 3.3V 전원 5V 전원 GPIO (디지털) GND (접지) 특수 기능 주요 GPIO 핀 번호 (BCM 방식) GPIO 2,3 → I2C (SDA, SCL) GPIO 14,15 → UART (TX, RX) GPIO 10,9,11 → SPI (MOSI, MISO, SCLK) GPIO 18 → PWM0 GPIO 12,13 → PWM GPIO 4 → 1-Wire 라즈베리파이 GPIO 핀 구조 BCM 번호 방식 기준 (Board 방식과 다름에 주의!)
💡 BCM vs BOARD 번호 방식 GPIO 핀을 부르는 방식이 두 가지야:
• BCM(Broadcom): 칩 내부 번호 기준 (GPIO 17, GPIO 18 등) — 대부분의 라이브러리 기본값
• BOARD: 물리적 핀 위치 번호 (1번~40번) — 보드 위치 기준
코드 짤 때 어떤 방식 쓰는지 명확히 해야 핀 번호 헷갈리지 않아!

💡 3. LED 제어 — Hello World of Hardware

프로그래밍 입문할 때 "Hello World" 찍는 것처럼, 하드웨어 입문은 LED 깜빡이기야 ㅋㅋㅋ
근데 이게 단순해 보여도 GPIO 제어의 핵심 원리가 다 들어있어. 진지하게 해보자!

🔌 회로 연결

LED 회로 연결 방법: 1. LED 긴 다리(+, 애노드) → 220Ω 저항 → GPIO 17번 핀
2. LED 짧은 다리(-, 캐소드) → GND 핀 (6번 또는 9번 등)

⚠️ 저항 없이 연결하면 LED 타거나 GPIO 핀 손상될 수 있어! 반드시 저항 달아야 해.

🐍 RPi.GPIO로 LED 제어

Python
import RPi.GPIO as GPIO
import time

# GPIO 핀 번호 방식 설정 (BCM 방식 사용)
GPIO.setmode(GPIO.BCM)

# GPIO 17번 핀을 출력으로 설정
GPIO.setup(17, GPIO.OUT)

try:
    while True:
        GPIO.output(17, GPIO.HIGH)  # LED 켜기
        print("LED ON 🔆")
        time.sleep(1)
        
        GPIO.output(17, GPIO.LOW)   # LED 끄기
        print("LED OFF 🌑")
        time.sleep(1)

except KeyboardInterrupt:
    print("프로그램 종료!")

finally:
    GPIO.cleanup()  # GPIO 핀 초기화 (매우 중요!)

🎯 gpiozero로 더 쉽게!

gpiozero 라이브러리는 코드가 훨씬 직관적이야. 진짜 Python스럽게 쓸 수 있거든:

Python
from gpiozero import LED
from time import sleep

led = LED(17)  # GPIO 17번에 연결된 LED

# 기본 제어
led.on()       # LED 켜기
sleep(1)
led.off()      # LED 끄기
sleep(1)

# 깜빡이기 (blink)
led.blink(on_time=0.5, off_time=0.5, n=10)
# on_time: 켜져있는 시간(초)
# off_time: 꺼져있는 시간(초)  
# n: 반복 횟수 (None이면 무한반복)

# 토글
led.toggle()   # 현재 상태 반전

🌈 PWM으로 LED 밝기 조절

PWM(Pulse Width Modulation)을 쓰면 LED 밝기를 0~100%로 조절할 수 있어.
디지털 신호를 빠르게 켰다 껐다 하면서 평균 전압을 조절하는 원리야:

Python
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)  # GPIO 18은 PWM 지원

# PWM 객체 생성 (핀 번호, 주파수(Hz))
pwm = GPIO.PWM(18, 1000)  # 1000Hz 주파수
pwm.start(0)  # 듀티 사이클 0%로 시작

try:
    # 서서히 밝아지기
    for duty in range(0, 101, 5):
        pwm.ChangeDutyCycle(duty)
        print(f"밝기: {duty}%")
        time.sleep(0.1)
    
    # 서서히 어두워지기
    for duty in range(100, -1, -5):
        pwm.ChangeDutyCycle(duty)
        time.sleep(0.1)

except KeyboardInterrupt:
    pass

finally:
    pwm.stop()
    GPIO.cleanup()
💡 gpiozero로 PWM LED 제어
from gpiozero import PWMLED
from time import sleep

led = PWMLED(18)

# 밝기 설정 (0.0 ~ 1.0)
led.value = 0.5   # 50% 밝기

# 서서히 밝아지는 효과
led.pulse(fade_in_time=2, fade_out_time=2)
gpiozero의 PWMLED는 pulse() 메서드 하나로 페이드 인/아웃 효과를 줄 수 있어. 진짜 편하지? 😎

🔘 4. 버튼 입력 처리 — 사용자와 소통하기

LED 켜는 건 출력(Output)이었고, 이번엔 입력(Input)을 다뤄볼 차례야.
버튼을 누르면 뭔가 일어나게 만드는 거 — 이게 인터랙티브 IoT의 시작이야!

🔌 버튼 회로 연결

풀업 저항 방식 (권장): • 버튼 한쪽 → GPIO 핀 (예: GPIO 2)
• 버튼 다른쪽 → GND
• 라즈베리파이 내부 풀업 저항 활성화 (소프트웨어로 설정)

풀다운 저항 방식:
• 버튼 한쪽 → GPIO 핀
• 버튼 다른쪽 → 3.3V
• 10kΩ 저항으로 GPIO 핀을 GND에 연결

🐍 버튼 폴링(Polling) 방식

Python
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

# 버튼 핀 설정 (내부 풀업 저항 활성화)
GPIO.setup(2, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# LED 핀 설정
GPIO.setup(17, GPIO.OUT)

print("버튼을 눌러보세요! (Ctrl+C로 종료)")

try:
    while True:
        # 버튼 상태 읽기
        # 풀업이라 평소엔 HIGH, 누르면 LOW
        button_state = GPIO.input(2)
        
        if button_state == GPIO.LOW:
            print("버튼 눌림! 💥")
            GPIO.output(17, GPIO.HIGH)  # LED 켜기
        else:
            GPIO.output(17, GPIO.LOW)   # LED 끄기
        
        time.sleep(0.05)  # CPU 과부하 방지

except KeyboardInterrupt:
    print("종료!")
finally:
    GPIO.cleanup()

⚡ 인터럽트(Interrupt) 방식 — 더 효율적!

폴링 방식은 계속 핀 상태를 체크하느라 CPU를 낭비해.
인터럽트 방식은 버튼이 눌릴 때만 함수가 호출되어서 훨씬 효율적이야:

Python
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(2, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(17, GPIO.OUT)

led_state = False

def button_callback(channel):
    """버튼이 눌릴 때 자동으로 호출되는 함수"""
    global led_state
    led_state = not led_state
    GPIO.output(17, led_state)
    print(f"LED {'ON 🔆' if led_state else 'OFF 🌑'}")

# 하강 엣지(HIGH→LOW) 감지 시 콜백 함수 호출
# bouncetime: 디바운싱 시간(ms) - 버튼 채터링 방지
GPIO.add_event_detect(
    2, 
    GPIO.FALLING, 
    callback=button_callback, 
    bouncetime=300
)

print("버튼으로 LED를 토글하세요! (Ctrl+C로 종료)")

try:
    while True:
        time.sleep(1)  # 메인 루프는 그냥 대기
except KeyboardInterrupt:
    print("종료!")
finally:
    GPIO.cleanup()
💡 gpiozero로 버튼 처리 (진짜 간단함 ㄷㄷ)
from gpiozero import Button, LED
from signal import pause

button = Button(2, pull_up=True, bounce_time=0.3)
led = LED(17)

# 버튼 눌릴 때 LED 켜기
button.when_pressed = led.on
# 버튼 뗄 때 LED 끄기
button.when_released = led.off

# 또는 토글
# button.when_pressed = led.toggle

print("버튼을 눌러보세요!")
pause()  # 프로그램 계속 실행 유지
코드가 진짜 영어 읽듯이 자연스럽지 않음? 이게 gpiozero의 매력이야 😍

🌡️ 5. 센서 데이터 읽기 — 세상을 감지하다

이제 진짜 IoT다운 걸 해볼 거야.
센서로 온도, 습도, 거리, 빛 등 실제 환경 데이터를 읽어오는 거거든.
이 데이터를 활용하면 스마트 홈, 환경 모니터링 시스템 같은 걸 만들 수 있어!

🌡️ DHT11/DHT22 온습도 센서

DHT11은 가장 많이 쓰이는 입문용 온습도 센서야.
DHT22는 DHT11보다 정확도가 높고 측정 범위도 넓어 (가격도 좀 더 비쌈).

항목DHT11DHT22
온도 범위0~50°C-40~80°C
온도 정확도±2°C±0.5°C
습도 범위20~90%0~100%
습도 정확도±5%±2~5%
샘플링 주기1초2초
가격약 1~2달러약 3~5달러
Terminal - 라이브러리 설치
pip3 install adafruit-circuitpython-dht
sudo apt install libgpiod2
Python - DHT 센서 읽기
import adafruit_dht
import board
import time

# DHT11 센서 초기화 (GPIO 4번 핀)
dht_device = adafruit_dht.DHT11(board.D4)

# DHT22 사용시: dht_device = adafruit_dht.DHT22(board.D4)

print("온습도 모니터링 시작! 🌡️")
print("-" * 40)

while True:
    try:
        # 온도와 습도 읽기
        temperature = dht_device.temperature
        humidity = dht_device.humidity
        
        print(f"온도: {temperature:.1f}°C | 습도: {humidity:.1f}%")
        
        # 온도 경고
        if temperature > 30:
            print("⚠️  온도가 높습니다! 환기 필요!")
        elif temperature < 10:
            print("🥶 온도가 낮습니다! 난방 필요!")
            
    except RuntimeError as error:
        # DHT 센서는 가끔 읽기 실패함 — 무시하고 재시도
        print(f"센서 읽기 실패: {error.args[0]}")
    
    time.sleep(2)  # DHT11은 최소 1초, DHT22는 최소 2초 간격

📏 HC-SR04 초음파 거리 센서

HC-SR04는 초음파를 쏘고 반사되어 돌아오는 시간으로 거리를 측정해.
측정 범위는 2cm ~ 400cm, 정확도는 약 ±3mm야.

⚡ 중요한 전압 주의사항! HC-SR04의 Echo 핀은 5V 신호를 출력하는데,
라즈베리파이 GPIO는 3.3V 입력만 허용해!
→ 전압 분배기(1kΩ + 2kΩ 저항) 또는 레벨 시프터 필수!

연결: Echo → 1kΩ → GPIO → 2kΩ → GND
Python - 초음파 거리 측정
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

TRIG = 23  # 트리거 핀
ECHO = 24  # 에코 핀

GPIO.setup(TRIG, GPIO.OUT)
GPIO.setup(ECHO, GPIO.IN)

def measure_distance():
    """초음파로 거리 측정 (cm 반환)"""
    # 트리거 신호 발사 (10μs 펄스)
    GPIO.output(TRIG, False)
    time.sleep(0.000002)
    GPIO.output(TRIG, True)
    time.sleep(0.00001)  # 10μs
    GPIO.output(TRIG, False)
    
    # 에코 신호 대기
    pulse_start = time.time()
    while GPIO.input(ECHO) == 0:
        pulse_start = time.time()
    
    pulse_end = time.time()
    while GPIO.input(ECHO) == 1:
        pulse_end = time.time()
    
    # 거리 계산
    # 음속: 343m/s = 34300cm/s
    # 왕복 거리이므로 2로 나눔
    pulse_duration = pulse_end - pulse_start
    distance = pulse_duration * 34300 / 2
    
    return round(distance, 2)

try:
    print("거리 측정 시작! 📏")
    while True:
        dist = measure_distance()
        print(f"거리: {dist} cm")
        
        if dist < 10:
            print("🚨 너무 가까워요!")
        elif dist < 30:
            print("⚠️  주의 거리")
        else:
            print("✅ 안전 거리")
        
        time.sleep(0.5)

except KeyboardInterrupt:
    print("종료!")
finally:
    GPIO.cleanup()

☀️ 조도 센서 (LDR + ADC)

라즈베리파이는 아날로그 입력이 없어서 LDR(광저항) 같은 아날로그 센서를 쓰려면
ADC(Analog-to-Digital Converter)가 필요해.
가장 많이 쓰는 건 MCP3008 (8채널 10비트 ADC, SPI 통신)이야:

Python - MCP3008 + LDR 조도 측정
import spidev
import time

# SPI 초기화
spi = spidev.SpiDev()
spi.open(0, 0)  # SPI 버스 0, 디바이스 0
spi.max_speed_hz = 1350000

def read_adc(channel):
    """MCP3008에서 아날로그 값 읽기 (0~1023)"""
    if channel < 0 or channel > 7:
        return -1
    
    adc = spi.xfer2([1, (8 + channel) << 4, 0])
    data = ((adc[1] & 3) << 8) + adc[2]
    return data

def get_light_percentage(channel=0):
    """조도를 퍼센트로 변환"""
    raw = read_adc(channel)
    # MCP3008은 0~1023 범위
    # LDR은 밝을수록 저항 감소 → 전압 증가
    percentage = (raw / 1023) * 100
    return round(percentage, 1)

try:
    print("조도 측정 시작! ☀️")
    while True:
        light = get_light_percentage()
        print(f"조도: {light}%")
        
        if light < 20:
            print("🌙 어둡습니다 — 조명 켜기!")
        elif light > 80:
            print("☀️  밝습니다 — 조명 끄기!")
        
        time.sleep(1)

except KeyboardInterrupt:
    print("종료!")
finally:
    spi.close()

⚙️ 6. 모터 제어 — 세상을 움직이다

센서로 데이터를 읽었으면, 이제 뭔가를 실제로 움직여볼 차례야!
IoT에서 가장 많이 쓰는 모터 종류와 Python으로 제어하는 방법을 알아보자.

IoT에서 자주 쓰는 모터 종류 DC 모터 연속 회전 PWM으로 속도 조절 L298N 드라이버 필요 🚗 RC카, 팬, 펌프 서보 모터 0~180° 각도 제어 PWM 신호로 제어 직접 GPIO 연결 가능 🦾 로봇팔, 카메라 팬틸트 스텝 모터 정밀 각도 제어 스텝 단위로 이동 ULN2003 드라이버 필요 🖨️ 3D프린터, CNC

🔄 서보 모터 제어

서보 모터는 PWM 신호로 각도를 제어해.
일반적으로 50Hz 주파수에서 듀티 사이클 2.5%~12.5%가 0°~180°에 해당해:

Python - 서보 모터 제어
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)

# 서보 PWM 설정 (50Hz)
servo = GPIO.PWM(18, 50)
servo.start(0)

def set_angle(angle):
    """서보 모터 각도 설정 (0~180도)"""
    # 각도를 듀티 사이클로 변환
    # 0° = 2.5%, 90° = 7.5%, 180° = 12.5%
    duty = 2.5 + (angle / 180) * 10
    servo.ChangeDutyCycle(duty)
    time.sleep(0.3)  # 모터가 움직일 시간
    servo.ChangeDutyCycle(0)  # 떨림 방지

try:
    print("서보 모터 제어 시작! 🦾")
    
    # 0도 → 90도 → 180도 → 0도
    for angle in [0, 45, 90, 135, 180, 90, 0]:
        print(f"각도: {angle}°")
        set_angle(angle)
        time.sleep(0.5)

except KeyboardInterrupt:
    print("종료!")
finally:
    servo.stop()
    GPIO.cleanup()
Python - gpiozero로 서보 제어
from gpiozero import Servo, AngularServo
from time import sleep

# AngularServo: 각도(-90 ~ 90) 직접 지정 가능
servo = AngularServo(
    18,
    min_angle=-90,
    max_angle=90,
    min_pulse_width=0.0005,  # 0.5ms
    max_pulse_width=0.0025   # 2.5ms
)

# 각도 설정
servo.angle = 0    # 중앙
sleep(1)
servo.angle = -90  # 최소
sleep(1)
servo.angle = 90   # 최대
sleep(1)
servo.angle = None  # 서보 비활성화 (전력 절약)

🚗 DC 모터 + L298N 드라이버

DC 모터는 GPIO 핀에서 직접 구동할 수 없어 (전류가 너무 많이 필요함).
L298N 같은 모터 드라이버 IC를 사용해야 해:

Python - L298N DC 모터 제어
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

# L298N 핀 설정
IN1 = 20  # 방향 제어 1
IN2 = 21  # 방향 제어 2
ENA = 16  # PWM (속도 제어)

GPIO.setup(IN1, GPIO.OUT)
GPIO.setup(IN2, GPIO.OUT)
GPIO.setup(ENA, GPIO.OUT)

# PWM 설정
motor_pwm = GPIO.PWM(ENA, 1000)
motor_pwm.start(0)

def motor_forward(speed):
    """모터 정방향 회전"""
    GPIO.output(IN1, GPIO.HIGH)
    GPIO.output(IN2, GPIO.LOW)
    motor_pwm.ChangeDutyCycle(speed)  # 0~100

def motor_backward(speed):
    """모터 역방향 회전"""
    GPIO.output(IN1, GPIO.LOW)
    GPIO.output(IN2, GPIO.HIGH)
    motor_pwm.ChangeDutyCycle(speed)

def motor_stop():
    """모터 정지"""
    GPIO.output(IN1, GPIO.LOW)
    GPIO.output(IN2, GPIO.LOW)
    motor_pwm.ChangeDutyCycle(0)

try:
    print("DC 모터 테스트! 🚗")
    
    print("정방향 50% 속도")
    motor_forward(50)
    time.sleep(2)
    
    print("정방향 100% 속도")
    motor_forward(100)
    time.sleep(2)
    
    print("역방향 75% 속도")
    motor_backward(75)
    time.sleep(2)
    
    print("정지")
    motor_stop()

except KeyboardInterrupt:
    print("종료!")
finally:
    motor_pwm.stop()
    GPIO.cleanup()

📡 7. I2C 통신 — 센서와 대화하기

I2C(Inter-Integrated Circuit)는 2개의 선(SDA, SCL)으로 여러 장치를 연결할 수 있는 통신 프로토콜이야.
라즈베리파이에서 OLED 디스플레이, 가속도계, 기압계 등 수많은 센서가 I2C를 사용해.

🔧 I2C 활성화

Terminal
# raspi-config로 I2C 활성화
sudo raspi-config
# Interface Options → I2C → Enable

# 또는 직접 설정
sudo nano /boot/config.txt
# dtparam=i2c_arm=on 추가

# I2C 도구 설치
sudo apt install i2c-tools python3-smbus

# 연결된 I2C 장치 스캔
sudo i2cdetect -y 1
# 주소가 표시되면 연결 성공!

📺 SSD1306 OLED 디스플레이

128x64 픽셀의 작은 OLED 디스플레이야. I2C로 연결하면 텍스트, 그래픽 다 표시 가능:

Terminal - 라이브러리 설치
pip3 install adafruit-circuitpython-ssd1306 pillow
Python - OLED 디스플레이 제어
import board
import busio
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont
import time

# I2C 초기화
i2c = busio.I2C(board.SCL, board.SDA)

# OLED 초기화 (128x64, I2C 주소 0x3C)
oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C)

# 화면 지우기
oled.fill(0)
oled.show()

# PIL로 이미지 생성
image = Image.new("1", (oled.width, oled.height))
draw = ImageDraw.Draw(image)

# 텍스트 그리기
draw.text((0, 0), "Hello, IoT!", fill=255)
draw.text((0, 16), "라즈베리파이", fill=255)
draw.text((0, 32), "Python 제어", fill=255)

# 사각형 그리기
draw.rectangle((0, 48, 127, 63), outline=255, fill=0)
draw.text((30, 50), "재능넷 IoT", fill=255)

# 디스플레이에 표시
oled.image(image)
oled.show()

print("OLED 디스플레이 출력 완료! 📺")
time.sleep(5)

# 화면 지우기
oled.fill(0)
oled.show()

📐 MPU-6050 가속도/자이로 센서

MPU-6050은 3축 가속도계 + 3축 자이로스코프가 합쳐진 센서야.
드론, 로봇, 게임 컨트롤러 등에 많이 쓰여:

Python - MPU-6050 데이터 읽기
import smbus2
import time
import math

# MPU-6050 레지스터 주소
MPU6050_ADDR = 0x68
PWR_MGMT_1 = 0x6B
ACCEL_XOUT_H = 0x3B
GYRO_XOUT_H = 0x43

bus = smbus2.SMBus(1)

# 센서 깨우기 (슬립 모드 해제)
bus.write_byte_data(MPU6050_ADDR, PWR_MGMT_1, 0)

def read_word_2c(addr):
    """2바이트 읽어서 2의 보수 변환"""
    high = bus.read_byte_data(MPU6050_ADDR, addr)
    low = bus.read_byte_data(MPU6050_ADDR, addr + 1)
    val = (high << 8) + low
    if val >= 0x8000:
        return -((65535 - val) + 1)
    return val

def get_accel():
    """가속도 데이터 읽기 (g 단위)"""
    ax = read_word_2c(ACCEL_XOUT_H) / 16384.0
    ay = read_word_2c(ACCEL_XOUT_H + 2) / 16384.0
    az = read_word_2c(ACCEL_XOUT_H + 4) / 16384.0
    return ax, ay, az

def get_gyro():
    """자이로 데이터 읽기 (°/s 단위)"""
    gx = read_word_2c(GYRO_XOUT_H) / 131.0
    gy = read_word_2c(GYRO_XOUT_H + 2) / 131.0
    gz = read_word_2c(GYRO_XOUT_H + 4) / 131.0
    return gx, gy, gz

def get_tilt_angle(ax, ay, az):
    """기울기 각도 계산"""
    roll = math.atan2(ay, az) * 180 / math.pi
    pitch = math.atan2(-ax, math.sqrt(ay**2 + az**2)) * 180 / math.pi
    return roll, pitch

print("MPU-6050 센서 읽기 시작! 📐")
while True:
    ax, ay, az = get_accel()
    gx, gy, gz = get_gyro()
    roll, pitch = get_tilt_angle(ax, ay, az)
    
    print(f"가속도: X={ax:.2f}g Y={ay:.2f}g Z={az:.2f}g")
    print(f"자이로: X={gx:.1f}°/s Y={gy:.1f}°/s Z={gz:.1f}°/s")
    print(f"기울기: Roll={roll:.1f}° Pitch={pitch:.1f}°")
    print("-" * 50)
    time.sleep(0.5)

🌐 8. MQTT로 IoT 네트워크 구축하기

이제 진짜 IoT다운 걸 해볼 거야 — 인터넷을 통한 원격 제어!
MQTT(Message Queuing Telemetry Transport)는 IoT에서 가장 많이 쓰이는 경량 메시지 프로토콜이야.
발행(Publish)/구독(Subscribe) 패턴으로 동작하고, 저전력·저대역폭 환경에 최적화되어 있어.

MQTT 발행/구독 구조 MQTT 브로커 (Mosquitto) 🌡️ 온도 센서 발행자 (Publisher) 📏 거리 센서 발행자 (Publisher) 🔘 버튼 발행자 (Publisher) 💡 LED 제어 구독자 (Subscriber) 📱 스마트폰 앱 구독자 (Subscriber) ☁️ 클라우드 DB 구독자 (Subscriber) 토픽: home/sensor/+ 토픽: home/control/+ 발행자와 구독자는 브로커를 통해 간접 통신 — 느슨한 결합(Loose Coupling)

🦟 Mosquitto 브로커 설치

Terminal
# Mosquitto MQTT 브로커 설치
sudo apt install mosquitto mosquitto-clients -y

# 서비스 시작 및 자동 시작 설정
sudo systemctl start mosquitto
sudo systemctl enable mosquitto

# 상태 확인
sudo systemctl status mosquitto

# 테스트 (터미널 2개 열어서)
# 터미널 1: 구독
mosquitto_sub -h localhost -t "test/topic"

# 터미널 2: 발행
mosquitto_pub -h localhost -t "test/topic" -m "Hello MQTT!"

🐍 Python MQTT 클라이언트

Terminal
pip3 install paho-mqtt
Python - MQTT 발행자 (센서 데이터 전송)
import paho.mqtt.client as mqtt
import json
import time
import random  # 실제로는 센서 라이브러리 사용

# MQTT 설정
BROKER = "localhost"  # 또는 원격 브로커 주소
PORT = 1883
TOPIC_TEMP = "home/sensor/temperature"
TOPIC_HUMID = "home/sensor/humidity"

# 클라이언트 생성
client = mqtt.Client(client_id="sensor_node_01")

def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("✅ MQTT 브로커 연결 성공!")
    else:
        print(f"❌ 연결 실패: {rc}")

def on_publish(client, userdata, mid):
    print(f"📤 메시지 발행 완료 (ID: {mid})")

client.on_connect = on_connect
client.on_publish = on_publish

# 브로커 연결
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()

print("센서 데이터 발행 시작! 📡")

try:
    while True:
        # 실제 환경에서는 DHT 센서에서 읽어옴
        temperature = round(20 + random.uniform(-5, 15), 1)
        humidity = round(50 + random.uniform(-20, 30), 1)
        
        # JSON 형태로 데이터 구성
        payload = json.dumps({
            "temperature": temperature,
            "humidity": humidity,
            "timestamp": time.time(),
            "device_id": "sensor_node_01"
        })
        
        # 발행 (QoS 1: 최소 1번 전달 보장)
        client.publish(TOPIC_TEMP, payload, qos=1)
        print(f"발행: 온도={temperature}°C, 습도={humidity}%")
        
        time.sleep(5)

except KeyboardInterrupt:
    print("종료!")
finally:
    client.loop_stop()
    client.disconnect()
Python - MQTT 구독자 (LED 원격 제어)
import paho.mqtt.client as mqtt
import json
from gpiozero import LED

led = LED(17)

BROKER = "localhost"
PORT = 1883
TOPIC_CONTROL = "home/control/led"
TOPIC_STATUS = "home/status/led"

client = mqtt.Client(client_id="led_controller_01")

def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("✅ 연결 성공!")
        # 연결 후 토픽 구독
        client.subscribe(TOPIC_CONTROL, qos=1)
        print(f"📥 구독 중: {TOPIC_CONTROL}")

def on_message(client, userdata, msg):
    """메시지 수신 시 호출"""
    try:
        payload = json.loads(msg.payload.decode())
        command = payload.get("command", "")
        
        if command == "on":
            led.on()
            print("💡 LED ON!")
            status = "on"
        elif command == "off":
            led.off()
            print("🌑 LED OFF!")
            status = "off"
        elif command == "toggle":
            led.toggle()
            status = "on" if led.is_lit else "off"
            print(f"🔄 LED 토글 → {status}")
        else:
            print(f"⚠️ 알 수 없는 명령: {command}")
            return
        
        # 상태 발행 (피드백)
        response = json.dumps({"status": status, "device": "led_01"})
        client.publish(TOPIC_STATUS, response, qos=1)
        
    except json.JSONDecodeError:
        print("❌ JSON 파싱 오류")

client.on_connect = on_connect
client.on_message = on_message

client.connect(BROKER, PORT, keepalive=60)

print("LED 원격 제어 대기 중... 🎮")
print("명령 예시: mosquitto_pub -t home/control/led -m '{\"command\":\"on\"}'")

client.loop_forever()  # 블로킹 루프 (Ctrl+C로 종료)
💡 MQTT QoS 레벨 이해하기 • QoS 0: 최대 1번 전달 (전달 보장 없음, 가장 빠름)
• QoS 1: 최소 1번 전달 (중복 가능, 일반적으로 사용)
• QoS 2: 정확히 1번 전달 (가장 안전, 오버헤드 큼)
IoT 센서 데이터는 보통 QoS 1, 중요한 제어 명령은 QoS 2를 써!

🏠 9. 실전 프로젝트 — 스마트 홈 시스템 만들기

지금까지 배운 걸 다 합쳐서 실제로 쓸 수 있는 스마트 홈 시스템을 만들어보자!
온습도 모니터링 + LED 자동 제어 + MQTT 원격 제어 + 웹 대시보드까지 — 진짜 IoT 프로젝트야 🏠

📋 프로젝트 구성

🏗️ 시스템 구성요소:
  • ✅ DHT22 온습도 센서 (GPIO 4)
  • ✅ LED x2 (GPIO 17: 상태 표시, GPIO 27: 경고)
  • ✅ 버튼 (GPIO 2: 수동 제어)
  • ✅ OLED 디스플레이 (I2C)
  • ✅ MQTT 브로커 (Mosquitto)
  • ✅ Flask 웹 서버 (원격 모니터링)
  • ✅ SQLite 데이터베이스 (데이터 저장)
Python - 스마트 홈 메인 시스템
#!/usr/bin/env python3
"""
스마트 홈 IoT 시스템
라즈베리파이 + Python + MQTT + Flask
"""

import threading
import time
import json
import sqlite3
import logging
from datetime import datetime
from gpiozero import LED, Button
import adafruit_dht
import board
import paho.mqtt.client as mqtt

# 로깅 설정
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s'
)
logger = logging.getLogger(__name__)

# ===== 하드웨어 초기화 =====
status_led = LED(17)
warning_led = LED(27)
manual_button = Button(2, pull_up=True, bounce_time=0.3)
dht_sensor = adafruit_dht.DHT22(board.D4)

# ===== 데이터베이스 초기화 =====
def init_database():
    conn = sqlite3.connect('smart_home.db')
    cursor = conn.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS sensor_data (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp TEXT NOT NULL,
            temperature REAL,
            humidity REAL,
            led_status TEXT
        )
    ''')
    conn.commit()
    conn.close()
    logger.info("데이터베이스 초기화 완료")

def save_sensor_data(temp, humid, led_status):
    conn = sqlite3.connect('smart_home.db')
    cursor = conn.cursor()
    cursor.execute(
        'INSERT INTO sensor_data (timestamp, temperature, humidity, led_status) VALUES (?, ?, ?, ?)',
        (datetime.now().isoformat(), temp, humid, led_status)
    )
    conn.commit()
    conn.close()

# ===== 시스템 상태 =====
system_state = {
    "temperature": 0.0,
    "humidity": 0.0,
    "led_on": False,
    "auto_mode": True,
    "last_update": None
}

# ===== MQTT 설정 =====
mqtt_client = mqtt.Client(client_id="smart_home_main")

def on_mqtt_connect(client, userdata, flags, rc):
    if rc == 0:
        logger.info("MQTT 브로커 연결 성공!")
        client.subscribe("home/control/#", qos=1)
    else:
        logger.error(f"MQTT 연결 실패: {rc}")

def on_mqtt_message(client, userdata, msg):
    topic = msg.topic
    try:
        payload = json.loads(msg.payload.decode())
        
        if topic == "home/control/led":
            cmd = payload.get("command")
            if cmd == "on":
                status_led.on()
                system_state["led_on"] = True
                system_state["auto_mode"] = False
            elif cmd == "off":
                status_led.off()
                system_state["led_on"] = False
                system_state["auto_mode"] = False
            elif cmd == "auto":
                system_state["auto_mode"] = True
                logger.info("자동 모드 활성화")
                
        logger.info(f"명령 수신: {topic} → {payload}")
    except Exception as e:
        logger.error(f"메시지 처리 오류: {e}")

mqtt_client.on_connect = on_mqtt_connect
mqtt_client.on_message = on_mqtt_message

# ===== 센서 읽기 스레드 =====
def sensor_thread():
    """5초마다 센서 데이터 읽기"""
    while True:
        try:
            temp = dht_sensor.temperature
            humid = dht_sensor.humidity
            
            if temp is not None and humid is not None:
                system_state["temperature"] = temp
                system_state["humidity"] = humid
                system_state["last_update"] = datetime.now().isoformat()
                
                # 자동 모드: 온도 기반 LED 제어
                if system_state["auto_mode"]:
                    if temp > 28:
                        status_led.on()
                        warning_led.blink(on_time=0.5, off_time=0.5)
                        system_state["led_on"] = True
                        logger.warning(f"고온 경고! {temp}°C")
                    else:
                        status_led.off()
                        warning_led.off()
                        system_state["led_on"] = False
                
                # MQTT로 데이터 발행
                sensor_data = json.dumps({
                    "temperature": round(temp, 1),
                    "humidity": round(humid, 1),
                    "led_status": "on" if system_state["led_on"] else "off",
                    "auto_mode": system_state["auto_mode"],
                    "timestamp": system_state["last_update"]
                })
                mqtt_client.publish("home/sensor/data", sensor_data, qos=1)
                
                # DB 저장
                save_sensor_data(
                    temp, humid,
                    "on" if system_state["led_on"] else "off"
                )
                
                logger.info(f"온도: {temp:.1f}°C | 습도: {humid:.1f}%")
                
        except RuntimeError as e:
            logger.warning(f"센서 읽기 실패: {e}")
        
        time.sleep(5)

# ===== 버튼 핸들러 =====
def button_handler():
    """수동 버튼으로 LED 토글"""
    system_state["auto_mode"] = False
    status_led.toggle()
    system_state["led_on"] = status_led.is_lit
    logger.info(f"버튼 눌림 → LED {'ON' if system_state['led_on'] else 'OFF'}")

manual_button.when_pressed = button_handler

# ===== 메인 실행 =====
if __name__ == "__main__":
    logger.info("🏠 스마트 홈 시스템 시작!")
    
    init_database()
    
    # MQTT 연결
    mqtt_client.connect("localhost", 1883, keepalive=60)
    mqtt_client.loop_start()
    
    # 센서 스레드 시작
    sensor_t = threading.Thread(target=sensor_thread, daemon=True)
    sensor_t.start()
    
    logger.info("시스템 가동 중... (Ctrl+C로 종료)")
    
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        logger.info("시스템 종료!")
    finally:
        mqtt_client.loop_stop()
        mqtt_client.disconnect()

🌐 Flask 웹 대시보드

Python - 웹 대시보드 서버
from flask import Flask, jsonify, render_template_string
import sqlite3
from datetime import datetime

app = Flask(__name__)

DASHBOARD_HTML = """
<!DOCTYPE html>
<html>
<head>
    <title>스마트 홈 대시보드</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        body { font-family: Arial; background: #f0f4ff; margin: 0; padding: 20px; }
        .card { background: white; border-radius: 12px; padding: 20px; margin: 10px; 
                box-shadow: 0 2px 8px rgba(0,0,0,0.1); display: inline-block; min-width: 150px; }
        .value { font-size: 2em; font-weight: bold; color: #5ba4cf; }
        h1 { color: #3a7ca5; }
    </style>
</head>
<body>
    <h1>🏠 스마트 홈 대시보드</h1>
    <div id="data">로딩 중...</div>
    <script>
        function updateData() {
            fetch('/api/status')
                .then(r => r.json())
                .then(data => {
                    document.getElementById('data').innerHTML = `
                        <div class="card">
                            <div>🌡️ 온도</div>
                            <div class="value">${data.temperature}°C</div>
                        </div>
                        <div class="card">
                            <div>💧 습도</div>
                            <div class="value">${data.humidity}%</div>
                        </div>
                        <div class="card">
                            <div>💡 LED</div>
                            <div class="value">${data.led_status}</div>
                        </div>
                    `;
                });
        }
        updateData();
        setInterval(updateData, 5000);
    </script>
</body>
</html>
"""

@app.route('/')
def dashboard():
    return render_template_string(DASHBOARD_HTML)

@app.route('/api/status')
def get_status():
    conn = sqlite3.connect('smart_home.db')
    cursor = conn.cursor()
    cursor.execute(
        'SELECT temperature, humidity, led_status, timestamp FROM sensor_data ORDER BY id DESC LIMIT 1'
    )
    row = cursor.fetchone()
    conn.close()
    
    if row:
        return jsonify({
            "temperature": row[0],
            "humidity": row[1],
            "led_status": row[2],
            "timestamp": row[3]
        })
    return jsonify({"error": "데이터 없음"})

@app.route('/api/history')
def get_history():
    conn = sqlite3.connect('smart_home.db')
    cursor = conn.cursor()
    cursor.execute(
        'SELECT temperature, humidity, timestamp FROM sensor_data ORDER BY id DESC LIMIT 50'
    )
    rows = cursor.fetchall()
    conn.close()
    
    return jsonify([
        {"temperature": r[0], "humidity": r[1], "timestamp": r[2]}
        for r in rows
    ])

if __name__ == '__main__':
    # 0.0.0.0으로 바인딩해야 외부에서 접속 가능
    app.run(host='0.0.0.0', port=5000, debug=False)
💡 웹 대시보드 접속 방법 라즈베리파이의 IP 주소를 확인하고 (hostname -I 명령어),
같은 네트워크의 스마트폰이나 PC에서 http://[라즈베리파이IP]:5000으로 접속하면 돼!
예: http://192.168.1.100:5000

🔒 10. 보안 및 최적화 — 진짜 프로처럼

IoT 시스템을 만들었으면 보안도 신경 써야 해.
인터넷에 연결된 장치는 해킹 타겟이 될 수 있거든 — 진짜로 ㄷㄷ

🔐 MQTT 보안 설정

Terminal - Mosquitto 인증 설정
# 비밀번호 파일 생성
sudo mosquitto_passwd -c /etc/mosquitto/passwd myuser

# Mosquitto 설정 파일 수정
sudo nano /etc/mosquitto/conf.d/default.conf
설정 파일 내용
# /etc/mosquitto/conf.d/default.conf
listener 1883 localhost
allow_anonymous false
password_file /etc/mosquitto/passwd

# TLS 설정 (외부 접속 시)
listener 8883
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
require_certificate false
Python - 인증 포함 MQTT 연결
import paho.mqtt.client as mqtt

client = mqtt.Client()

# 사용자 인증 설정
client.username_pw_set("myuser", "mypassword")

# TLS 설정 (외부 브로커 사용 시)
# client.tls_set(ca_certs="/path/to/ca.crt")

client.connect("localhost", 1883, keepalive=60)

⚡ 성능 최적화 팁

항목문제해결책
CPU 과부하폴링 루프가 CPU 100% 사용time.sleep() 추가, 인터럽트 방식 사용
메모리 누수GPIO 정리 안 됨finally 블록에 GPIO.cleanup() 필수
센서 오류DHT 읽기 실패try-except로 예외 처리, 재시도 로직
DB 성능매번 연결/해제연결 풀링 또는 WAL 모드 사용
전력 소비불필요한 연산슬립 모드, 이벤트 기반 처리
Python - 안정적인 센서 읽기 패턴
import time
import logging

logger = logging.getLogger(__name__)

def read_sensor_with_retry(sensor, max_retries=3, delay=2):
    """재시도 로직이 포함된 안정적인 센서 읽기"""
    for attempt in range(max_retries):
        try:
            temp = sensor.temperature
            humid = sensor.humidity
            
            if temp is None or humid is None:
                raise ValueError("센서 값이 None")
            
            # 유효 범위 검증
            if not (-40 <= temp <= 80):
                raise ValueError(f"온도 범위 초과: {temp}")
            if not (0 <= humid <= 100):
                raise ValueError(f"습도 범위 초과: {humid}")
            
            return temp, humid
            
        except (RuntimeError, ValueError) as e:
            logger.warning(f"센서 읽기 실패 ({attempt+1}/{max_retries}): {e}")
            if attempt < max_retries - 1:
                time.sleep(delay)
    
    logger.error("최대 재시도 횟수 초과!")
    return None, None

🔄 systemd로 자동 시작 설정

라즈베리파이가 재부팅되어도 IoT 시스템이 자동으로 시작되게 하려면 systemd 서비스를 등록해야 해:

Terminal
sudo nano /etc/systemd/system/smart-home.service
서비스 파일 내용
[Unit]
Description=Smart Home IoT System
After=network.target mosquitto.service
Wants=mosquitto.service

[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/smart-home
ExecStart=/usr/bin/python3 /home/pi/smart-home/main.py
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
Terminal - 서비스 등록 및 시작
sudo systemctl daemon-reload
sudo systemctl enable smart-home.service
sudo systemctl start smart-home.service

# 상태 확인
sudo systemctl status smart-home.service

# 로그 확인
sudo journalctl -u smart-home.service -f

🚀 11. 더 나아가기 — 심화 주제들

기본기를 다 익혔으면 이제 더 깊이 들어가볼 수 있어.
IoT + Python의 세계는 진짜 무궁무진하거든 🌌

📊 InfluxDB + Grafana로 데이터 시각화

시계열 데이터 모니터링 스택: • InfluxDB: IoT 시계열 데이터에 최적화된 DB
• Grafana: 아름다운 실시간 대시보드
• Telegraf: 데이터 수집 에이전트

라즈베리파이에서 직접 구동하거나, 별도 서버에 설치해서 MQTT로 데이터 전송 가능!
Python - InfluxDB에 데이터 저장
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
import time

# InfluxDB 연결 설정
INFLUXDB_URL = "http://localhost:8086"
INFLUXDB_TOKEN = "your-token-here"
INFLUXDB_ORG = "my-org"
INFLUXDB_BUCKET = "smart-home"

client = InfluxDBClient(
    url=INFLUXDB_URL,
    token=INFLUXDB_TOKEN,
    org=INFLUXDB_ORG
)
write_api = client.write_api(write_options=SYNCHRONOUS)

def write_sensor_data(temperature, humidity, device_id="pi_01"):
    """InfluxDB에 센서 데이터 저장"""
    point = (
        Point("environment")
        .tag("device", device_id)
        .tag("location", "living_room")
        .field("temperature", temperature)
        .field("humidity", humidity)
    )
    write_api.write(bucket=INFLUXDB_BUCKET, record=point)
    print(f"InfluxDB 저장: {temperature}°C, {humidity}%")

# 사용 예시
write_sensor_data(25.3, 60.5)

🤖 머신러닝으로 이상 감지

Python - 간단한 이상 감지 모델
import numpy as np
from sklearn.ensemble import IsolationForest
import pickle

# 정상 데이터로 모델 훈련
def train_anomaly_detector(normal_data):
    """
    normal_data: [[temp1, humid1], [temp2, humid2], ...] 형태
    """
    model = IsolationForest(
        contamination=0.1,  # 이상치 비율 예상값
        random_state=42
    )
    model.fit(normal_data)
    
    # 모델 저장
    with open('anomaly_model.pkl', 'wb') as f:
        pickle.dump(model, f)
    
    print("이상 감지 모델 훈련 완료!")
    return model

def detect_anomaly(model, temperature, humidity):
    """
    반환값: True = 이상, False = 정상
    """
    data = np.array([[temperature, humidity]])
    prediction = model.predict(data)
    return prediction[0] == -1  # -1이면 이상치

# 사용 예시
# 정상 데이터 수집 (실제로는 DB에서 읽어옴)
normal_data = np.array([
    [22.1, 55.2], [22.5, 56.1], [23.0, 54.8],
    [21.8, 57.3], [22.3, 55.9], [23.1, 56.5]
])

model = train_anomaly_detector(normal_data)

# 실시간 감지
test_temp, test_humid = 45.0, 95.0  # 비정상적인 값
if detect_anomaly(model, test_temp, test_humid):
    print(f"🚨 이상 감지! 온도:{test_temp}°C 습도:{test_humid}%")
else:
    print("✅ 정상 범위")

📱 Telegram 봇으로 알림 보내기

Python - Telegram 알림 봇
import requests
import json

class TelegramNotifier:
    def __init__(self, bot_token, chat_id):
        self.bot_token = bot_token
        self.chat_id = chat_id
        self.base_url = f"https://api.telegram.org/bot{bot_token}"
    
    def send_message(self, message):
        """텍스트 메시지 전송"""
        url = f"{self.base_url}/sendMessage"
        payload = {
            "chat_id": self.chat_id,
            "text": message,
            "parse_mode": "HTML"
        }
        response = requests.post(url, json=payload)
        return response.json()
    
    def send_alert(self, temperature, humidity):
        """온도/습도 경고 알림"""
        message = (
            f"🚨 <b>스마트 홈 경고!</b>\n\n"
            f"🌡️ 온도: <b>{temperature:.1f}°C</b>\n"
            f"💧 습도: <b>{humidity:.1f}%</b>\n\n"
            f"⏰ 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        )
        return self.send_message(message)

# 사용 예시
# BotFather에서 봇 토큰 받고, 채팅 ID 확인 후 사용
notifier = TelegramNotifier(
    bot_token="YOUR_BOT_TOKEN",
    chat_id="YOUR_CHAT_ID"
)

# 고온 감지 시 알림
if temperature > 35:
    notifier.send_alert(temperature, humidity)
    print("📱 Telegram 알림 전송!")
💡 재능넷에서 IoT 프로젝트 공유하기 이런 IoT 프로젝트 개발 능력을 재능넷(jaenung.net)에서 재능으로 등록해보는 건 어때?
스마트 홈 시스템 구축, 라즈베리파이 세팅, IoT 센서 연동 등의 서비스를 제공하면
실제로 수익을 창출할 수 있어! 하드웨어 + 소프트웨어 능력은 희소성이 높거든 💰

📚 12. 트러블슈팅 — 막혔을 때 이렇게 해!

IoT 개발하다 보면 진짜 별의별 문제가 다 생겨 ㅋㅋㅋ
자주 만나는 문제들과 해결책을 정리해봤어:

문제원인해결책
GPIO 핀이 반응 없음핀 번호 방식 혼동 (BCM/BOARD)GPIO.setmode() 확인, 핀 번호 재확인
DHT 센서 계속 실패전선 연결 불량, 타이밍 문제연결 확인, 2초 이상 간격, 재시도 로직
I2C 장치 인식 안 됨I2C 비활성화, 주소 충돌raspi-config에서 I2C 활성화, i2cdetect 확인
MQTT 연결 실패브로커 미실행, 방화벽systemctl status mosquitto, 포트 확인
서보 모터 떨림PWM 신호 유지명령 후 ChangeDutyCycle(0) 호출
Python 권한 오류GPIO 접근 권한 없음sudo 실행 또는 gpio 그룹 추가
라즈베리파이 과열CPU 과부하, 방열판 없음방열판/쿨러 장착, 코드 최적화
Terminal - 유용한 디버깅 명령어
# GPIO 상태 확인
gpio readall

# I2C 장치 스캔
sudo i2cdetect -y 1

# SPI 장치 확인
ls /dev/spi*

# 시스템 온도 확인 (라즈베리파이 CPU 온도)
vcgencmd measure_temp

# 메모리 사용량
free -h

# CPU 사용률
top

# 프로세스 확인
ps aux | grep python

# 포트 사용 확인
sudo netstat -tlnp | grep 1883
⚠️ 자주 하는 실수 TOP 3 1️⃣ GPIO.cleanup() 빠뜨리기: 프로그램 종료 시 GPIO 초기화 안 하면 다음 실행 때 경고 뜨거나 핀 상태가 이상해질 수 있어.

2️⃣ 전압 레벨 무시하기: 5V 신호를 3.3V GPIO에 직접 연결하면 라즈베리파이 손상! 레벨 시프터 필수.

3️⃣ 전원 용량 부족: 모터, 서보 여러 개 연결할 때 GPIO 전원만으로는 부족해. 외부 전원 공급 필요!

🎯 마무리 — 이제 진짜 IoT 개발자다!

🚀 IoT 개발 여정 완주! 라즈베리파이 × Python × IoT ✅ GPIO 제어 ✅ LED/버튼 ✅ 센서 읽기 ✅ 모터 제어 ✅ I2C 통신 ✅ MQTT 네트워크 ✅ 웹 대시보드 ✅ 실전 프로젝트 현실 세계를 코드로 움직이는 진짜 개발자 🎉

와 진짜 긴 여정이었는데 여기까지 왔으면 박수 한 번 쳐도 됨 👏👏👏
오늘 우리가 다룬 내용을 정리해보면:

📋 오늘 배운 것들 총정리: 🔹 IoT 개념과 라즈베리파이 하드웨어 이해
🔹 개발 환경 세팅 (OS 설치, Python 라이브러리)
🔹 GPIO 핀 이해 (BCM/BOARD 방식)
🔹 LED 제어 (디지털 출력, PWM 밝기 조절)
🔹 버튼 입력 처리 (폴링, 인터럽트 방식)
🔹 센서 데이터 읽기 (DHT22, HC-SR04, LDR)
🔹 모터 제어 (서보, DC 모터)
🔹 I2C 통신 (OLED, MPU-6050)
🔹 MQTT 프로토콜로 IoT 네트워크 구축
🔹 스마트 홈 실전 프로젝트
🔹 보안 설정 및 systemd 자동 시작
🔹 InfluxDB, Grafana, Telegram 봇 심화

IoT는 진짜 무한한 가능성의 세계야.
스마트 홈, 농업 자동화, 공장 모니터링, 환경 감시, 로봇 제어...
Python이라는 강력한 언어와 라즈베리파이라는 저렴하고 강력한 하드웨어가 있으면
상상하는 거의 모든 걸 만들 수 있어.

그리고 이런 능력을 갈고 닦아서 재능넷같은 플랫폼에서 재능으로 공유하면
실제 수익도 창출할 수 있다는 거 잊지 마! 💪

자, 이제 브레드보드 꺼내고 코드 에디터 열어봐 — 세상을 움직일 시간이야! 🌍⚡

🐍 Python 🍓 라즈베리파이 🌐 IoT ⚡ GPIO 📡 MQTT 🌡️ 센서 ⚙️ 모터제어 🏠 스마트홈 🔧 하드웨어 💻 임베디드
댓글 작성

이 글에 대한 여러분의 생각을 들려주세요

댓글 0
댓글을 불러오는 중입니다.

아직 댓글이 없습니다.