소음이 많은 환경에 전화 이벤트 라즈베리파이 피코 W 활용하기

기계 가동 소음이 심한 공장에서 근무하시는 부모님께서는 스마트폰의 벨 소리나 진동을 인지하기 어렵다. 급한 연락을 놓치는 상황이 반복되자, 시중에 판매되는 조명이나 증폭기를 찾아보았으나 원격 제어만 가능할 뿐 ‘전화 이벤트’와 연동되는 적절한 제품을 찾기 힘들었다. 이에 서랍 속에 있던 라즈베리파이 피코 W와 안드로이드 자동화 앱인 Tasker를 결합하여, 전화가 오면 강력하게 깜빡이는 시각적 알림 장치를 직접 제작해 보았다.


1. 준비물 및 핵심 원리

  • 하드웨어: 라즈베리파이 피코 W (내장 LED 활용)

  • 소프트웨어: Thonny 에디터, MicroPython, Tasker (안드로이드 유료 앱, 약 7,500원)

  • 작동 원리:

    1. 스마트폰에 전화가 오면 Tasker가 이벤트를 감지한다.

    2. Tasker가 블루투스(BLE)를 통해 피코 W에 특정 신호(RING)를 전송한다.

    3. 신호를 받은 피코 W는 즉시 내장 LED를 점멸시켜 시각적 알림을 제공한다.

    4. 통화가 시작되거나 종료되면 종료 신호(STOP)를 보내 LED를 끄고 대기 상태로 돌아간다.

    • 포인트: 전화를 받지 못한 부재중 상태에서도 사용자가 확인할 때까지 LED를 계속 점멸시키도록 설정했다.


2. 단계별 제작 가이드

① 1단계: 라즈베리파이 피코 W 코딩 (MicroPython)

피코 W는 무선 칩을 내장하고 있어 MicroPython의 bluetooth 라이브러리를 통해 통신이 가능하다. Thonny 에디터를 사용하여 아래와 같은 로직을 구현했다.

  • 주요 로직: Nordic UART Service (NUS) UUID를 설정하여 스마트폰과 데이터를 주고받는다. RING 수신 시 논블로킹 패턴으로 LED를 점멸시키고, STOP 수신 시 점멸을 멈춘다.

  • 코드 저장: 작성한 코드를 피코 W 내부에 main.py라는 이름으로 저장하여 전원이 공급되면 자동으로 실행되도록 한다.

import bluetooth
import machine
import utime
from micropython import const

_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_WRITE = const(3)

# ===== Nordic UART Service (NUS) UUIDs =====
NUS_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
RX_UUID  = bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E")  # Phone -> Pico (WRITE)
TX_UUID  = bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")  # Pico -> Phone (NOTIFY)

_FLAG_WRITE  = bluetooth.FLAG_WRITE
_FLAG_NOTIFY = bluetooth.FLAG_NOTIFY

led = machine.Pin("LED", machine.Pin.OUT)

# ===== 논블로킹 패턴 + 타임아웃 =====
PATTERN_RING = [
    (1, 120),
    (0, 120),
]

pattern = PATTERN_RING
pattern_idx = 0
next_change_ms = utime.ticks_ms()
blinking = False

RING_TIMEOUT_MS = 0   # 0이면 무한
ring_started_ms = None


def stop_blink():
    global blinking, ring_started_ms
    blinking = False
    ring_started_ms = None
    led.off()


def start_blink():
    global blinking, ring_started_ms, pattern_idx, next_change_ms
    blinking = True
    ring_started_ms = utime.ticks_ms()
    pattern_idx = 0
    next_change_ms = utime.ticks_ms()


def pattern_tick():
    global pattern_idx, next_change_ms

    now = utime.ticks_ms()

    if blinking and RING_TIMEOUT_MS and ring_started_ms is not None:
        if utime.ticks_diff(now, ring_started_ms) >= RING_TIMEOUT_MS:
            print("RING timeout -> auto STOP")
            stop_blink()
            return

    if not blinking:
        return

    if utime.ticks_diff(now, next_change_ms) >= 0:
        on, dur = pattern[pattern_idx]
        led.value(on)
        pattern_idx = (pattern_idx + 1) % len(pattern)
        next_change_ms = utime.ticks_add(now, dur)


class PicoNUS:
    def __init__(self):
        self.ble = bluetooth.BLE()
        self.ble.active(True)
        self.ble.irq(self._irq)

        # NUS: RX(write), TX(notify)
        ((self.rx_handle, self.tx_handle),) = self.ble.gatts_register_services((
            (
                NUS_UUID,
                (
                    (RX_UUID, _FLAG_WRITE),
                    (TX_UUID, _FLAG_NOTIFY),
                ),
            ),
        ))

        self._advertise()

    def _irq(self, event, data):
        if event == _IRQ_CENTRAL_CONNECT:
            print("BLE Connected")

        elif event == _IRQ_CENTRAL_DISCONNECT:
            print("BLE Disconnected")
            stop_blink()
            self._advertise()

        elif event == _IRQ_GATTS_WRITE:
            raw = self.ble.gatts_read(self.rx_handle)
            try:
                cmd = raw.decode().strip().upper()
            except Exception:
                cmd = ""

            print("BLE Received:", cmd)

            if cmd in ("RING", "CALL", "START"):
                start_blink()
            elif cmd in ("STOP", "END", "IDLE", "OFF"):
                stop_blink()

    def _advertise(self):
        name = b"Pico-Call-LED"
        adv = (
            b"\x02\x01\x06" +
            bytes((len(name) + 1, 0x09)) +
            name
        )
        self.ble.gap_advertise(100_000, adv)
        print("Advertising as Pico-Call-LED (NUS)")


PicoNUS()
stop_blink()

while True:
    pattern_tick()
    utime.sleep_ms(5)

 

② 2단계: Tasker 앱 연동 설정

별도의 앱 개발 없이 블루투스 연동을 가능하게 해주는 안드로이드의 강력한 자동화 앱 Tasker를 활용한다.

  1. 프로필 생성: ‘전화 벨 울림’ 이벤트를 트리거로 설정한다.

  2. 작업(Task) 할당: BLE Tasker Plugin을 사용하여 피코 W의 MAC 주소와 UUID를 입력하고 연결(Connect) 및 메시지 전송(Send Message: RING) 단계를 구성한다.

  3. 종료 설정: ‘통화 중’ 또는 ‘통화 종료’ 시 STOP 신호를 보내도록 추가 프로필을 구성하여 LED를 제어한다.


3. 실사용 후기 및 보완점

공장 한편, 부모님의 시야가 잘 닿는 곳에 알루미늄 반사판과 함께 설치해 드렸다. 결과는 대만족이었다.

  • 장점: LED 불빛 덕분에 소음 속에서도 전화를 즉시 확인하실 수 있게 되었다. 부재중 상황에서도 LED가 계속 깜빡이고 있어 나중에라도 연락을 주시는 빈도가 높아졌다.

  • 보완할 점: 내장 LED의 광량이 공장 내부 환경에 따라 다소 작게 느껴질 수 있다. 더 확실한 알림을 원한다면 외부 고휘도 LED를 추가로 연결하여 보완하는 것이 좋다.


거창한 시스템은 아니지만, 부모님의 실질적인 불편함을 해소해 드렸다는 점에서 매우 보람 있는 프로젝트였다. 기성품 중 마땅한 대안이 없다면 저렴하고 강력한 라즈베리파이 피코 W를 활용해 보는 것을 적극 추천한다. 본인의 환경에 맞춰 코드를 커스텀할 수 있다는 것이 DIY의 가장 큰 매력이다.

댓글 남기기