일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 티스토리챌린지
- homebridge
- RS-485
- SK텔레콤
- 나스닥
- 홈네트워크
- raspberry pi
- 공모주
- Espressif
- 매터
- ConnectedHomeIP
- 코스피
- 오블완
- 파이썬
- 애플
- 현대통신
- cluster
- Apple
- 미국주식
- 국내주식
- 배당
- Bestin
- MQTT
- matter
- 힐스테이트 광교산
- 월패드
- Home Assistant
- 해외주식
- esp32
- Python
- Today
- Total
YOGYUI
힐스테이트 광교산::주방 비디오폰 연동 - 세대 및 공동 현관문 제어 (애플 홈킷) 본문
주방에 설치되어 있는 (주)동영엠텍의 DM-D5102QMS 주방용 TV폰의 RS-485 패킷 해석 및 응용 관련 글을 2개 올린 바 있다
힐스테이트 광교산::주방 비디오폰 RS-485 패킷 해석
힐스테이트 광교산::주방 비디오폰 연동 - HEMS(에너지 모니터링)
주방 비디오폰은 HEMS (에너지 모니터링)보다는 세대 현관문 및 공동 현관문의 호출 시 영상 및 음성 통화 + 문열림 기능이 주된 편의기능이다
세대/공동 현관문과 주방 비디오폰간의 물리적 거리 및 호출 시 다른 세대(옆집)에 초인종 소리로 인한 소음 문제 등 패킷 캡쳐 실험을 하는데 제약사항이 많아서 텀을 길게 두고 코드를 조금씩~조금씩 구현해왔다
(거의 2달정도 걸린듯 ㅎㅎ)
아직 모든 기능이 완벽하게 연동된 건 아니지만, 지금까지 작업한 내용을 기록으로 남길 겸 포스팅해본다
1. Hardware
UTP 케이블의 핀맵은 지난번 HEMS에서 언급한 바와 마찬가지다
- 1번 (주+흰): GND
- 2번 (주): SVS > Composite Video 시그널
- 3번 (녹+흰): LS2 > 음성 신호로 추정
- 4번 (청): SC2B > RS-485 통신선(B)
- 5번 (청+흰): SC2A > RS-485 통신선(A)
- 6번 (녹): LS1 > 음성 신호로 추정
- 7번 (갈+흰): GND
- 8번 (갈): SUB12 > 현관 도어폰 전원 (+12V, 주방 비디오폰에서는 의미없음)
결선도는 다음과 같다
Homebridge 서버로 쓰던 SBC(Raspberry Pi)를 주방 전자레인지 위에 올려두고 구현 및 테스트를 진행했다
주방이 너무 지저분해졌는데, 추후 주방 비디오폰 연동용 임베디드 시스템을 따로 구현할 생각!
2. State Machine Design
세대 현관문과 공동 현관문을 호출하고, 통화하거나 혹은 문열림 버튼을 누르면서 주방 비디오폰의 제어 상태를 위와 같이 간단히 그려봤다
각 상태별 간단한 설명은 다음과 같다
- IDLE: 휴지(休止) 상태
- FRONT DOOR RINGING: 세대 현관문 초인종이 울리고 있는 상태
- COMMUNAL DOOR RINGING: 공동 현관문 초인종이 울리고 있는 상태
- A/V STREAMING: 세대 현관문 혹은 공동 현관문과 통화중 (영상 및 음성 송출)인 상태
- UNLOCK FRONT/COMMUNAL DOOR: 세대 현관문 혹은 공동 현관문 잠금 해제 상태
개인적인 실험으로는 세대 현관문과 공동 현관문이 동시에 호출되는 경우를 혼자서는 물리적으로 만들 수가 없어서 상태를 분리해뒀는데, 실제 구동 환경은 어떨런지 잘 모르겠다 ㅋㅋ
※ 세대 현관문은 초인종 호출 없이도 집안 내부에서 주방 비디오폰으로 통화중 상태로 진입한 뒤 문열림 상태로 진입이 가능한 것이 특징인데, 이를 통해 스마트폰으로 현관 도어락을 해제할 수 있다
- 일전에 I/O 방식으로 도어락 해제 기능을 구현했는데, 좀 더 스마트해질 수 있다!
힐스테이트 광교산::현관 도어락 - 애플 홈킷 + 구글 어시스턴트 연동
3. RS-485 패킷 명세
주방 비디오폰과 거실 월패드가 주고받는 패킷 중 세대 현관문/공동 현관문 제어와 관련된 녀석들을 추출해서 위에서 그린 State Machine Diagram과 매칭시켜보자
패킷 포맷: 7F XY 00 00 EE
요약: 2번째 바이트의 상위 4비트(X) 값이 0xB이면 세대현관문, 0x5 혹은 0x6이면 공동현관문 관련 패킷
패킷 | 통신 방향 | 상태 전이 |
7F B4 00 00 EE | 비디오폰 → 월패드 | FRONT DOOR RINGING → UNLOCK FRONT/COMMUNAL DOOR |
7F B5 00 00 EE | 월패드 → 비디오폰 | IDLE → FRONT DOOR RINGING |
7F B6 00 00 EE | 월패드 → 비디오폰 | FRONT DOOR RINGING → IDLE |
7F B7 00 00 EE | 비디오폰 → 월패드 | FRONT DOOR RINGING → A/V STREAMING |
7F B8 00 00 EE | 비디오폰 → 월패드 | FRONT DOOR RINGING → IDLE |
7F B9 00 00 EE | 비디오폰 → 월패드 | IDLE → A/V STREAMING |
7F BA 00 00 EE | 비디오폰 → 월패드 | A/V STREAMING → IDLE |
7F 5A 00 00 EE | 월패드 → 비디오폰 | IDLE → COMMUNAL DOOR RINGING |
7F 5C 00 00 EE | 월패드 → 비디오폰 | COMMUNALDOOR RINGING → IDLE |
7F 5E 00 00 EE | 월패드 → 비디오폰 | A/V STREAMING → IDLE |
7F 5F 00 00 EE | 비디오폰 → 월패드 | COMMUNALDOOR RINGING → A/V STREAMING |
7F 60 00 00 EE | 비디오폰 → 월패드 | A/V STREAMING → IDLE |
7F 61 00 00 EE | 비디오폰 → 월패드 | A/V STREAMING → UNLOCK FRONT/COMMUNAL DOOR |
※ 반복 실험의 한계(소음;;) 때문에 정확하지 않을 가능성이 크다 ^^;;
특히 세대 현관문 쪽 상태 전이는 헷갈리는 점이 많았는데, 네이버 카페의 글을 많이 참고했다 (회원 가입 필요..)
4. Python 코드 구현
호출된 상태에서 영상을 수신하는 통화중 상태로 갈지, 그냥 호출을 종료시킬 지, 문열림 상태로 갈지 등 3가지 분기로 진행할 수 있으니, 각 상황에 맞게 코드를 조건문을 활용해서 구현해줘야 한다
(특히 세대 현관문 제어는 도어락 제어랑 현동시킬 수 있다)
우선 '호출' 및 '도어락 잠금 상태'에 대한 열거형 클래스를 선언해주자
from enum import IntEnum
class StateRinging(IntEnum):
# 서브폰의 호출 상태
IDLE = 0
FRONT = 1 # 현관문 초인종
COMMUNAL = 2 # 공동출입문
class StateDoorLock(IntEnum):
Unsecured = 0
Secured = 1
Jammed = 2
Unknown = 3
4.1. 주방 비디오폰 디바이스 클래스
주방 비디오폰 클래스 SubPhone은 내부 멤버변수로
state_streaming(통화중) / state_ringing(초인종 호출중) / state_doorlock(문열림)
3가지 상태값을 별개로 나누어 관리하게 구현했다 (MQTT 액세서리별로 publish할 때 이게 좀 더 코드가 깔끔하더라...)
class SubPhone(Device):
state_streaming: int = 0
state_ringing: StateRinging = StateRinging.IDLE
state_doorlock: StateDoorLock = StateDoorLock.Secured
def __init__(self, name: str = 'SubPhone', **kwargs):
super().__init__(name, **kwargs)
# for ffmpeg configuration
self.streaming_config = {
'conf_file_path': '',
'feed_path': '',
'input_device': '/dev/video0',
'frame_rate': 24,
'width': 320,
'height': 240,
}
def publish_mqtt(self):
topic = self.mqtt_publish_topic
if self.mqtt_client is not None:
obj = {"state": self.state_streaming}
self.mqtt_client.publish(topic + '/streaming', json.dumps(obj), 1)
if self.state_ringing in [StateRinging.FRONT, StateRinging.COMMUNAL]:
self.mqtt_client.publish(topic + '/doorbell', 'ON', 1)
else:
self.mqtt_client.publish(topic + '/doorbell', 'OFF', 1)
obj = {"state": self.state_doorlock.name} # 도어락은 상태 조회가 안되고 '열기' 기능만 존재한다
self.mqtt_client.publish(topic + '/doorlock', json.dumps(obj), 1)
def updateState(self, state: int, **kwargs):
streaming = kwargs.get('streaming')
if streaming is not None:
self.state_streaming = streaming
self.sig_state_streaming.emit(self.state_streaming)
self.publish_mqtt()
ringing_front = kwargs.get('ringing_front')
if ringing_front is not None:
if ringing_front:
self.state_ringing = StateRinging.FRONT
else:
self.state_ringing = StateRinging.IDLE
self.publish_mqtt()
ringing_communal = kwargs.get('ringing_communal')
if ringing_communal is not None:
if ringing_communal:
self.state_ringing = StateRinging.COMMUNAL
else:
self.state_ringing = StateRinging.IDLE
self.publish_mqtt()
doorlock = kwargs.get('doorlock')
if doorlock is not None:
self.state_doorlock = StateDoorLock(doorlock)
self.publish_mqtt()
def makePacketCommon(self, header: int) -> bytearray:
return bytearray([0x7F, max(0, min(0xFF, header)), 0x00, 0x00, 0xEE])
def makePacketSetVideoStreamingState(self, state: int) -> bytearray:
if state:
if self.state_ringing == StateRinging.FRONT:
# 현관 초인종 카메라 영상 서브폰 우회
return self.makePacketCommon(0xB7)
elif self.state_ringing == StateRinging.COMMUNAL:
# 공동현관문 영상 우회
return self.makePacketCommon(0x5F)
else:
# 단순 문열기용 (주방 서브폰 활성화)
return self.makePacketCommon(0xB9)
else:
if self.state_ringing == StateRinging.FRONT:
# 현관 초인종 카메라 영상 서브폰 우회 종료
return self.makePacketCommon(0xB8)
elif self.state_ringing == StateRinging.COMMUNAL:
# 공동현관문 영상 우회 종료
return self.makePacketCommon(0x60)
else:
# 단순 문열기용 (주방 서브폰 활성화)
return self.makePacketCommon(0xBA)
def makePacketOpenFrontDoor(self) -> bytearray:
# 현관 초인종 카메라 영상이 서브폰으로 우회된 상태에서 도어락 해제
return self.makePacketCommon(0xB4)
def makePacketOpenCommunalDoor(self) -> bytearray:
# 공동현관문 호출 후 카메라 영상이 우회된 상태에서 열림 명령
return self.makePacketCommon(0x61)
4.2. RS-485 패킷 파서 클래스
파서 클래스의 패킷 해석 구문은 위에서 설계한 State Machine 및 SubPhone 클래스의 상태 변수 값을 바꾸기 위해 적절히 dict key를 설정해서 콜백하게 만들어줬다
class ParserSubPhone(PacketParser):
def handlePacket(self):
# 3840 baudrate
# packet format: 7F XX XX XX EE
idx = self.buffer.find(0x7F)
if idx > 0:
self.buffer = self.buffer[idx:]
if len(self.buffer) >= 2:
idx2 = self.buffer.find(0xEE)
if idx2 > 0:
self.line_busy = False
packet = self.buffer[:idx2 + 1]
self.interpretPacket(packet)
self.buffer = self.buffer[idx2 + 1:]
def interpretPacket(self, packet: bytearray):
if (packet[1] & 0xF0) == 0xB0: # 현관 도어폰 호출
self.handleFrontDoor(packet)
elif (packet[1] & 0xF0) == 0x50: # 공동 현관문 호출
self.handleCommunalDoor(packet)
def handleFrontDoor(self, packet: bytearray):
result = {'device': 'subphone'}
notify: bool = True
if packet[1] == 0xB5:
# 현관 도어폰 초인종 호출 (월패드 -> 서브폰)
result['ringing_front'] = 1
elif packet[1] == 0xB6:
# 현관 도어폰 초인종 호출 종료 (월패드 -> 서브폰)
result['ringing_front'] = 0
elif packet[1] == 0xB9:
# 서브폰에서 현관 통화 시작 (서브폰 -> 월패드)
result['streaming'] = 1
elif packet[1] == 0xBA:
# 서브폰에서 현관 통화 종료 (서브폰 -> 월패드)
result['streaming'] = 0
elif packet[1] == 0xB4:
# 서브폰에서 현관문 열림 명령 (서브폰 -> 월패드)
result['doorlock'] = 0 # Unsecured
elif packet[1] in [0xBB, 0xB8]:
# 현관 도어폰 통화 종료
result['ringing_front'] = 0
result['streaming'] = 0
result['doorlock'] = 1 # Secured
else:
notify = False
if notify:
self.sig_parse_result.emit(result)
def handleCommunalDoor(self, packet: bytearray):
result = {'device': 'subphone'}
notify: bool = True
if packet[1] == 0x5A:
# 공동현관문 호출 (월패드 -> 서브폰)
result['ringing_communal'] = 1
elif packet[1] == 0x5C:
# 공동현관문 호출 종료 (월패드 -> 서브폰)
result['ringing_communal'] = 0
elif packet[1] == 0x5E:
# 공동현관문 통화 종료
result['ringing_communal'] = 0
result['streaming'] = 0
result['doorlock'] = 1 # Secured
else:
notify = False
if notify:
self.sig_parse_result.emit(result)
4.3. FFMpeg 및 FFServer 런타임 구동
FFMpeg와 FFServer를 외부에서 구동하려고 하니 라즈베리파이 재부팅 시 자동 실행 옵션도 넣어야하고, 구동되고 있는지 여부를 별도로 쿼리해야하는 등 귀찮은 점들이 있어서 홈네트워크 앱이 실행될 때 같이 실행되도록 코드를 변경했다
단순히 subprocess를 활용해서 메인 이벤트 루프에서 호출했더니 이벤트 루프 전체를 잡아먹길래, FFMpeg와 FFServer를 각각 다른 프로세스에서 돌릴 수 있게 multiprocessing을 활용해서 개별 프로세스에서 subprocess가 구동될 수 있게 꾸며줬다
- 여러개의 서브프로세스를 멀티프로세싱할 수 있는 약간의 고급 구현법
(멀티프로세싱에 익숙하지 않은 개발자라면 어질어질할 수 있지만, 최대한 가독성을 살려서 구현해봤다 ㅎㅎ...) - 코드 내부 서브프로세스 호출 명령어가 모두 "exec"로 시작한다
- 프로세스 정상 종료 위해 메인 이벤트 루프로 Pipe를 활용해서 각 프로세스별 PID 값을 넘겨준다
import subprocess
import multiprocessing as mp
from multiprocessing import connection
def procFFMpeg(cfg: dict, pipe: connection.Connection):
pid = os.getpid()
name = mp.current_process().name
idev = cfg.get('input_device')
fps = cfg.get('frame_rate')
width = cfg.get('width')
height = cfg.get('height')
feed = cfg.get('feed_path')
cmd = f"exec ~/ffmpeg/ffmpeg -i {idev} -r {fps} -s {width}x{height} -threads 1 {feed}"
with subprocess.Popen(cmd,
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT
) as subproc:
pid = subproc.pid
msg = f'{pid}'
pipe.send_bytes(msg.encode(encoding='utf-8', errors='ignore'))
buff = subproc.stdout.read()
print(buff.decode(encoding='UTF-8'))
def procFFServer(cfg: dict, pipe: connection.Connection):
pid = os.getpid()
name = mp.current_process().name
prefix = f'[MultiProcess][{name}({pid})] '
conf_path = cfg.get('conf_file_path')
cmd = f'exec ~/ffmpeg/ffserver -f {conf_path}'
with subprocess.Popen(cmd,
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT
) as subproc:
pid = subproc.pid
msg = f'{pid}'
pipe.send_bytes(msg.encode(encoding='utf-8', errors='ignore'))
buff = subproc.stdout.read()
print(buff.decode(encoding='UTF-8'))
import psutil
import multiprocessing
class Home:
mp_ffserver: Union[multiprocessing.Process, None] = None
pid_ffserver_proc: int = 0
mp_ffmpeg: Union[multiprocessing.Process, None] = None
pid_ffmpeg_proc: int = 0
def initialize(self, init_service: bool, connect_rs485: bool):
if self.rs485_subphone_config.enable:
self.startFFServer()
self.startFFMpeg()
def release(self):
if self.rs485_subphone_config.enable:
self.stopFFMpeg()
self.stopFFServer()
def startFFServer(self):
pipe1, pipe2 = multiprocessing.Pipe(duplex=True)
args = [self.subphone.streaming_config, pipe1]
self.mp_ffserver = multiprocessing.Process(target=procFFServer, name='FFServer', args=tuple(args))
self.mp_ffserver.start()
while True:
if pipe2.poll():
recv = pipe2.recv_bytes().decode(encoding='utf-8', errors='ignore')
writeLog(f'Recv from FFServer process pipe: {recv}', self)
self.pid_ffserver_proc = int(recv)
break
def stopFFServer(self):
if self.mp_ffserver is not None:
psutil.Process(self.pid_ffserver_proc).kill()
self.mp_ffserver.terminate()
writeLog(f'FFServer Process Terminated', self)
self.mp_ffserver = None
def startFFMpeg(self):
pipe1, pipe2 = multiprocessing.Pipe(duplex=True)
args = [self.subphone.streaming_config, pipe1]
self.mp_ffmpeg = multiprocessing.Process(target=procFFMpeg, name='FFMpeg', args=tuple(args))
self.mp_ffmpeg.start()
while True:
if pipe2.poll():
recv = pipe2.recv_bytes().decode(encoding='utf-8', errors='ignore')
writeLog(f'Recv from FFMpeg process pipe: {recv}', self)
self.pid_ffmpeg_proc = int(recv)
break
proc = psutil.Process(self.pid_ffmpeg_proc)
proc.nice(0)
def stopFFMpeg(self):
if self.mp_ffmpeg is not None:
psutil.Process(self.pid_ffmpeg_proc).kill()
self.mp_ffmpeg.terminate()
writeLog(f'FFMpeg Process Terminated', self)
self.mp_ffmpeg = None
전체 코드는 언제나처럼 깃허브 저장소에 커밋해뒀다
https://github.com/YOGYUI/HomeNetwork/tree/main/Hillstate-Gwanggyosan
5. Homebridge 액세서리 추가
주방 비디오폰의 영상 수신을 위해 Camera-FFMpeg 플러그인을 활용했다
https://github.com/Sunoo/homebridge-camera-ffmpeg#readme
{
"platform": "Camera-ffmpeg",
"mqtt": "mqtt broker host address",
"portmqtt": 1883,
"usermqtt": "mqtt broker auth id",
"passmqtt": "mqtt broker auth password",
"cameras": [
{
"name": "doorphone video",
"manufacturer": "Hyundai HT",
"model": "HDC-100S",
"motion": false,
"doorbell": true,
"switches": false,
"unbridge": false,
"videoConfig": {
"source": "-i {ffserver_streaming_address}",
"maxStreams": 2,
"maxWidth": 640,
"maxHeight": 480,
"maxFPS": 60,
"audio": false
},
"mqtt": {
"doorbellTopic": "home/hillstate/subphone/state/doorbell",
"doorbellMessage": "ON"
}
}
]
}
또한, 통화중 상태를 알아보기 위한 스위치 및 문열림 기능 연동을 위해 MQTT-Thing 플러그인의 'switch'와 'lockMechanism' 항목을 추가해줬다
https://github.com/arachnetech/homebridge-mqttthing#readme
{
"accessory": "mqttthing",
"type": "switch",
"name": "SubPhone Door Camera",
"url": "mqtt broker host address",
"username": "mqtt broker auth id",
"password": "mqtt broker auth password",
"topics": {
"getOn": {
"topic": "home/hillstate/subphone/state/streaming",
"apply": "return (JSON.parse(message).state == 1);"
},
"setOn": {
"topic": "home/hillstate/subphone/command/streaming",
"apply": "return JSON.stringify({state: message});"
}
},
"integerValue": true,
"onValue": 1,
"offValue": 0,
"logMqtt": false
},
{
"accessory": "mqttthing",
"type": "lockMechanism",
"name": "SubPhone DoorLock",
"url": "mqtt broker host address",
"username": "mqtt broker auth id",
"password": "mqtt broker auth password",
"topics": {
"getLockCurrentState": {
"topic": "home/hillstate/subphone/state/doorlock",
"apply": "return JSON.parse(message).state;"
},
"getLockTargetState": {
"topic": "home/hillstate/subphone/state/doorlock",
"apply": "return JSON.parse(message).state;"
},
"setLockTargetState": {
"topic": "home/hillstate/subphone/command/doorlock",
"apply": "return JSON.stringify({state: message});"
}
},
"lockValues": [
"Unsecured",
"Secured",
"Jammed",
"Unknown"
],
"logMqtt": false
}
6. DEMO
필수 기능들을 연동한 결과물을 영상으로 확인해보자
6.1. 세대현관문 통화 (카메라 연동)
주방 비디오폰에서 현관문 통화 모드 진입
상태 전이 흐름: IDLE → A/V STREAMING
애플 기기에서 현관문 통화 모드 진입
상태 전이 흐름: IDLE → A/V STREAMING
※ 주방 비디오폰이 도어폰과 통화중일 때는 거실 월패드에 '서브폰과 도어폰 통화 중입니다'라는 메시지가 팝업되는 것을 알 수 있다
6.2. 공동현관문 통화 (카메라 연동)
지하 6층 주차장에서 세대 호출을 누른 뒤 아이폰으로 영상을 확인해봤다
상태 전이 흐름: IDLE → COMMUNAL DOOR RINGING → A/V STREAMING
물론 애플 기기로 공동현관문의 문도 열 수 있다 (1층 공동현관문에서 문열림 기능을 찍어봤다)
상태 전이 흐름: IDLE → COMMUNAL DOOR RINGING → A/V STREAMING → UNLOCK DOOR → IDLE
※ 세대현관문 도어락은 초인종 호출이 되지 않은 상태에서도 문열림이 가능하지만, 공동현관문은 초인종 호출 상태에서만 통화중 상태로 넘어간 뒤 문열림이 가능하다
- 아무래도 공동현관문은 여러 개가 있는데다, 영상 자체가 단지 네트워크를 통해 IP 카메라 형식으로 넘어오기 때문인 것으로 추측된다
6.3. 현관문 도어락 잠금해제
아이폰으로 방문자의 얼굴을 실시간으로 확인할 수 있다
집안에서 애플 기기로 현관 도어락 잠금을 해제할 수 있다 (별도의 액세서리로 빠져있기 때문에 조금 불편하긴 하지만, Siri로 음성 명령이 가능하기 때문에 큰 문제는 없다 ㅎㅎ)
상태 전이 흐름: IDLE → FRONT DOOR RINGING → A/V STREAMING → UNLOCK DOOR → IDLE
현관문 앞에서 아이폰으로 도어락 잠금 해제도 가능하다
상태 전이 흐름: IDLE → A/V STREAMING → UNLOCK DOOR → IDLE
※ 통화 상태로 잠깐 변경되면서 도어폰의 LED 조명이 잠깐 켜지는 것을 알 수 있다
도어락 지문 인식이 잘 안될때 아주 편리하다
홈킷 도어락 액세서리는 휴대폰 잠금이 해제된 상태에서만 제어가 가능하기 때문에 스마트폰 분실에 따른 집안 도난 우려는 살짝 접어둬도 되지... 않을까? ㅋㅋ
7. TODO
UTP 케이블의 3번 핀(LS2)와 6번 핀(LS1)은 음성 신호인 것 같아 오실로스코프로 찍어봤다
노란색(CH1)이 LS1, 파란색(CH2)이 LS2 신호이며, 빨간색은 Math로 (CH1 - CH2) 계산 결과이다
약 60Hz의 정현파가 각각 출력되며, 주방 비디오폰의 마이크 부분을 손으로 문질렀을 때 Math 결과를 보면 파형의 변동이 있는 것을 알 수 있었다
즉, 일반 음성 신호가 60Hz의 differential 신호로 변조(modulation)되어 송/수신되는 것이 아닌가 의심해볼 만 하다.. differential 신호를 변조/복조하려면 OP-AMP 회로가 필요할 것 같은데, 테스트용으로 쓸만한게 없어서 시도를 못하고 있다 ㅎㅎ
어차피 지금까지 해온 내용을 토대로 임베디드 솔루션을 만들어볼 계획을 하고 있으니, 음성 연동은 프로젝트 시작 즈음에 테스트해볼까 생각중이다
이제 집에 탑재되어 있는 홈 IoT 연동 작업은 거의 끝난 것 같다
물론 단지 네트워크 통신을 후킹해서 차량 도착이나 주차 위치같은 유용한 정보도 연동이 가능하다고는 하는데, openwrt를 거의 다뤄본 적도 없을 뿐더러 거실 월패드 벽면에 공간이 협소에서 SBC 하나 놓을 공간도 부족하기 때문에 보류!
슬슬 마무리짓고 Summary 포스팅해야겠다..
끝~!
'홈네트워크(IoT) > 힐스테이트 광교산' 카테고리의 다른 글
힐스테이트 광교산::주방 싱크대 LED 조명 IoT 연동 (2) | 2022.11.19 |
---|---|
LG 물걸레 로봇청소기 (코드제로 M9) + 싱크대 절수 페달 연동 (ThinQ + Homebridge) (0) | 2022.10.29 |
힐스테이트 광교산::주방 비디오폰 연동 - HEMS(에너지 모니터링) (0) | 2022.10.25 |
힐스테이트 광교산::싱크대 절수페달 IoT 연동하기 - Final (0) | 2022.09.30 |
힐스테이트 광교산::싱크대 절수페달 IoT 연동하기 - (3) (0) | 2022.09.18 |