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

저희 부모님께서는 기계 가동 소음이 심한 공장에서 근무하고 계십니다. 그러다 보니 스마트폰 벨 소리나 진동을 전혀 인지하지 못해 급한 전화를 놓치는 경우가 종종 발생했습니다. 가끔 급하게 연락을 드려야 할 때도 거의 퇴근 시간쯤 연락을 해주실 때가 많았는데요. 좋은 방법이 없을까 고민하다가 이런 니즈에 맞춰 나온 상품이 없나 찾아보았지만 품질이 좋아 보이진 않는 중국산 벨 소리 증폭기 정도만 있었고 LED 같은 상품이 없을까 찾아보았지만 원격으로 ON/OFF 정도만 가능한 조명밖에 없어 전화 이벤트를 알리기엔 적합하지 않았습니다. 그러다 문득 예전에 장난감 삼아 사둔 라즈베리 파이 피코 W 가 떠올랐고 작지만 내장 LED까지 탑재하고 있어 어느 정도 시각적으로 알림이 가능하지 않을까 생각하다가 아무래도 블루투스를 연동하려면 앱을 개발해야 되니 시간이 좀 걸리겠구나 생각을 했었는데… 관련해서 정보를 찾아보니 안드로이드 자동화 앱인 Tasker를 활용하면 앱 개발 없이도 블루투스 연동이 가능했습니다. 그래서 AI한테 물어보며 직접 제작한 전화가 오면 LED가 강력하게 깜빡이는 알림 장치를 제작한 과정을 공유합니다.

<빛 반사를 위해 알루미늄 반사판을 배경으로 만들어둠>

준비물 및 핵심 원리

  • 하드웨어: 라즈베리파이 피코 W(내장 LED 사용)
  • 소프트웨어: Thonny 에디터, MicroPython, Tasker (Android 앱, 유료 7,500원)
  • 작동 원리:
    1. 스마트폰에 전화가 오면 Tasker가 이벤트를 감지합니다.
    2. Tasker가 블루투스(BLE)를 통해 피코 W에 특정 신호를 전송합니다.(RING)
    3. 신호를 받은 피코 W가 즉시 LED를 점멸시켜 시각적 알림을 제공합니다.
    4. 마찬가지로 통화 중 상태가 되면 특정 신호를 전송하여 종료시킵니다.(STOP) 
    5. 신호를 받은 피코 W가 LED를 끄고 다음 이벤트를 대기합니다.

여기서 포인트는 전화를 받지 못한 상태 부재중의 경우 3번에 계속 머무르며 전화를 걸 때까지 LED를 계속 점멸시킵니다.


단계별 제작 가이드

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

피코 W는 블루투스와 와이파이를 수신할 수 있는 무선칩을 내장하고 있으므로 MicroPython의 bluetooth 라이브러리를 사용했습니다.
  1. 우선 thonny 에디터를 열어 라즈베리파이피코w와 연결 시킵니다.
  2. 아래 코드를 작성합니다.(GPT를 통해 코드를 작성시킴)
  3. 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  # 부재중 알림 점멸 지속 시간(ms) 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:
                    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)
  4. main.py 이름으로 저장 후 종료

2단계: Tasker 앱 연동 설정

안드로이드의 Tasker 앱은 스마트폰의 다양한 이벤트를 트리거 시켜 강력한 자동화 기능을 제공합니다.
  1. Playstore 에서 Tasker를 다운 받습니다.(유료 7,500원)
  2. BLE Tasker plugin 이라는 Tasker의 플러그인 앱도 다운받습니다.
  3. 아래와 같이 Tasker 앱을 실행 시켜 설정해줍니다.
    • 프로필 탭에서 +버튼을 누르기
    • 만들기
    • 이벤트
    • 전화
    • 전화벨 울림
    • 새로운 작업 – RING
      • + 버튼
        • 플러그인
        • BLE Tasker Plugin
        • Disconnect Action(최초에 연결이 되어있는 경우 끊고 시작을 위함)
          • 오류 후 작업 계속 체크
      • + 버튼
        • 플러그인
        • BLE Tasker Plugin
        • Connect Action
          • 구성에서 연필 모양 편집 버튼
          • MAC 주소 입력
          • 아래 세 항목에 UUID 입력(코드의 6E400001-B5A3-F393-E0A9-E50E24DCCA9E 값)
            • GATT Service UUID
            • Characteristic for receive message event
            • Characteristic for sending messages
      • + 버튼
        • 플러그인
        • BLE Tasker Plugin
        • Send Message Action
          • 구성에서 연필 모양 편집 버튼
          • Text 타입 선택
          • Message to send 에 RING 작성
    • 다시 프로필로 돌아와서 + 추가 버튼
    • 이벤트
    • 전화
    • 통화 중
    • 새로운 작업 – STOP
      • + 버튼
        • 플러그인
        • BLE Tasker Plugin
        • Send Message Action
          • 구성에서 연필 모양 편집 버튼
          • Text 타입 선택
          • Message to send 에 STOP 작성
      • + 버튼
        • 플러그인
        • BLE Tasker Plugin
        • Disconnect Action(최초에 연결이 되어있는 경우 끊고 시작을 위함)
          • 오류 후 작업 계속 체크
  4. 완료 후 피코W 와 연결되는지 테스트

실사용 후기

공장 한편 부모님 시야가 잘 닿는 곳에 LED 장치를 설치해 드렸습니다. 이제는 LED 불빛을 보고 전화를 잘 받으시고 혹시나 못받는 상황이 되어도 LED는 계속 들어오고 있으니 확인하시고 너무 늦지 않게 다시 전화를 해주시곤 하십니다.
직접 제작해 보니 기성품보다 저렴할 뿐만 아니라, 부모님의 환경에 딱 맞게 커스텀할 수 있다는 점이 큰 보람이었습니다. 다만, 내장 LED 의 경우 너무 작아 아쉬운 부분이 있는데 더 큰 LED가 필요하신분은 외부 LED를 추가로 달아서 보완은 가능할 것 같습니다.

거창한 시스템은 아니지만, 부모님의 불편함을 해소해 드릴 수 있어 보람 있는 프로젝트였습니다. 비슷한 고민을 하시는 분들이라면 라즈베리파이 피코 W를 활용해 보시길 적극 추천합니다. 궁금하신 점은 댓글로 남겨주세요!

댓글 남기기