일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- esp32
- 티스토리챌린지
- MQTT
- Bestin
- raspberry pi
- 힐스테이트 광교산
- 해외주식
- 국내주식
- Python
- 홈네트워크
- 월패드
- 공모주
- 코스피
- 나스닥
- Home Assistant
- ConnectedHomeIP
- 파이썬
- 오블완
- 매터
- 미국주식
- cluster
- 배당
- Apple
- 현대통신
- homebridge
- RS-485
- matter
- SK텔레콤
- 애플
- Espressif
- Today
- Total
YOGYUI
힐스테이트 광교산::조명 제어 RS-485 패킷 분석 (2) 본문
5. 외부 제어 (앱) 설치
외부에서 홈네트워크 명령을 주는 용도로 사용하기 위해 SMARTHOME Hi-OT (힐스테이트 스마트홈) 앱을 설치했다
스마트폰 앱으로 제어 연동할 수 있는건 나름 노력해서 잘 만든것 같은데, 단점이 한두개가 아닌 것 같다 ㅋㅋ
- 방 이름 변경을 할 수가 없다 (설정 창에서 방이름 변경 시 키보드 레이아웃이 안뜬다... 블루투스 키보드 사용자만 변경하라는건가? ㅋㅋㅋ
- 제어 반응성이 썩 좋지않다 (변경사항이 적용될 때까지 메시지박스가 서버로부터 응답을 기다리는 동안 다른 작업을 할 수가 없다)
아무래도 안드로이드랑 아이폰 둘 다 동일한 인터페이스로 만들다보니 발생한 문제같은데... (UI가 iOS에 특화된 컨트롤들이 아니다) 기업 입장에서야 인건비 아끼는건 뭐라 할게 아니지만 사용자 입장에서 성의가 없어보이는건 어쩔 수 없다 ㅎㅎ
어쨌든 외부에서 제어 명령 내렸을 때 패킷 후킹용으로는 적당한 것 같다
6. 제어 명령 후 패킷 변경 내용 확인
컴퓨터방(방3)의 조명을 켜고 끄는 명령을 앱을 통해 내렸을 때 다음과 같은 변경사항을 확인했다
[평상시]
F7 0B 01 19 01 40 40 00 00 E5 EE
[조명 켜는 명령 시]
F7 0B 01 19 02 40 41 01 00 E6 EE
[조명 끄는 명령 시]
F7 0B 01 19 02 40 41 02 00 E5 EE
오호라... 이제서야 감이 확실하게 잡힌다
3, 4번째 바이트가 [0x01, 0x19]인 패킷의 5번째 패킷이 0x01일 경우 현재 상태를 쿼리하게 되며, 0x02일 경우가 상태를 바꾸는 명령이 된다
동일하게 각 방별로 상태를 바꾸면서 패킷을 모아보자
장소 | 명령 | 패킷 |
거실 | 평상시 (쿼리) | F7 0B 01 19 01 40 10 00 00 B5 EE |
조명 1 ON | F7 0B 01 19 02 40 11 01 00 B6 EE | |
조명 1 OFF | F7 0B 01 19 02 40 11 02 00 B5 EE | |
조명 2 ON | F7 0B 01 19 02 40 12 01 00 B5 EE | |
조명 2 OFF | F7 0B 01 19 02 40 12 02 00 B6 EE | |
조명 3 ON | F7 0B 01 19 02 40 13 01 00 B4 EE | |
조명 3 OFF | F7 0B 01 19 02 40 13 02 00 B7 EE | |
조명 모두 ON | F7 0B 01 19 02 40 11 01 00 B6 EE F7 0B 01 19 02 40 12 01 00 B5 EE F7 0B 01 19 02 40 13 01 00 B4 EE |
|
조명 모두 OFF | F7 0B 01 19 02 40 11 02 00 B5 EE F7 0B 01 19 02 40 12 02 00 B6 EE F7 0B 01 19 02 40 13 02 00 B7 EE |
|
침실(방1) | 평상시 (쿼리) | F7 0B 01 19 01 40 20 00 00 85 EE |
조명 1 ON | F7 0B 01 19 02 40 21 01 00 86 EE | |
조명 1 OFF | F7 0B 01 19 02 40 21 02 00 85 EE | |
조명 2 ON | F7 0B 01 19 02 40 22 01 00 85 EE | |
조명 2 OFF | F7 0B 01 19 02 40 22 02 00 86 EE | |
조명 모두 ON | F7 0B 01 19 02 40 21 01 00 86 EE F7 0B 01 19 02 40 22 01 00 85 EE |
|
조명 모두 OFF | F7 0B 01 19 02 40 21 02 00 85 EE F7 0B 01 19 02 40 22 02 00 86 EE |
|
서재(방2) | 평상시 (쿼리) | F7 0B 01 19 01 40 30 00 00 95 EE |
조명 ON | F7 0B 01 19 02 40 31 01 00 96 EE | |
조명 OFF | F7 0B 01 19 02 40 31 02 00 95 EE | |
컴퓨터방(방3) | 평상시 (쿼리) | F7 0B 01 19 01 40 40 00 00 E5 EE |
조명 ON | F7 0B 01 19 02 40 41 01 00 E6 EE | |
조명 OFF | F7 0B 01 19 02 40 41 02 00 E5 EE | |
주방 | 평상시 (쿼리) | F7 0B 01 19 01 40 60 00 00 C5 EE |
조명 1 ON | F7 0B 01 19 02 40 61 01 00 C6 EE | |
조명 1 OFF | F7 0B 01 19 02 40 61 02 00 C5 EE | |
조명 2 ON | F7 0B 01 19 02 40 62 01 00 C5 EE | |
조명 2 OFF | F7 0B 01 19 02 40 62 02 00 C6 EE | |
조명 모두 ON | F7 0B 01 19 02 40 61 01 00 C6 EE F7 0B 01 19 02 40 62 01 00 C5 EE |
|
조명 모두 OFF | F7 0B 01 19 02 40 61 02 00 C5 EE F7 0B 01 19 02 40 62 02 00 C6 EE |
- 3,4번 바이트가 [0x01, 0x19]일 때, 5번째 바이트가 0x02이면 조명 상태를 변경하는 명령이 된다
- 7번째 바이트의 상위 4비트는 장소 인덱스가 된다
0x1□ = 거실, 0x2□ = 침실(방1), 0x3□ = 서재(방2), 0x4□ = 컴퓨터방(방3), 0x6□ = 주방 - 7번째 바이트의 하위 4비트는 제어하고자 하는 전등의 인덱스가 된다
0x□1 = 첫번째 전등, 0x□2 = 두번째 전등, 0x□3 = 세번째 전등 - 조명 일괄 ON/OFF에 대한 별도의 명령은 없고, 각 조명별 제어 명령을 순차적으로 전송할 뿐이다
- 8번째 바이트가 0x01이면 조명을 켜는 명령, 0x02이면 조명을 끄는 명령이 된다
7. 체크섬 바이트 계산
패킷의 뒤에서 2번째 바이트값은 Xor 방식의 체크섬이 아닐까 생각되어 앞서 후킹한 패킷으로 계산해봤다 (XOR 계산 시작값을 0으로 설정)
Hexa | Binary | XOR Step | Hexa | Binary | XOR Step | Hexa | Binary | XOR Step |
F7 | 1111 0111 | 1111 0111 | F7 | 1111 0111 | 1111 0111 | F7 | 1111 0111 | 1111 0111 |
0B | 0000 1011 | 1111 1100 | 0B | 0000 1011 | 1111 1100 | 0C | 0000 1100 | 1111 1011 |
01 | 0000 0001 | 1111 1101 | 01 | 0000 0001 | 1111 1101 | 01 | 0000 0001 | 1111 1010 |
19 | 0001 1001 | 1110 0100 | 19 | 0001 1001 | 1110 0100 | 19 | 0001 1001 | 1110 0011 |
01 | 0000 0001 | 1110 0101 | 01 | 0000 0001 | 1110 0101 | 04 | 0000 0100 | 1110 0111 |
40 | 0100 0000 | 1010 0101 | 40 | 0100 0000 | 1010 0101 | 40 | 0100 0000 | 1010 0111 |
10 | 0001 0000 | 1011 0101 | 20 | 0010 0000 | 1000 0101 | 60 | 0110 0000 | 1100 0111 |
00 | 0000 0000 | 1011 0101 | 00 | 0000 0000 | 1000 0101 | 00 | 0000 0000 | 1100 0111 |
00 | 0000 0000 | 1011 0101 | 00 | 0000 0000 | 1000 0101 | 02 | 0000 0010 | 1100 0101 |
B5 | 1011 0101 | 85 | 1000 0101 | 02 | 0000 0020 | 1100 0111 | ||
EE | 1110 1110 | EE | 1110 1110 | C7 | 1100 0111 | |||
EE | 1110 1110 |
XOR Sum 방식으로 계산하다보면, 패킷의 시작 바이트 (0xF7)부터 순차적으로 계산한 값이 패킷의 뒤에서 두번째 패킷과 일치하는 것을 알 수 있다
여러 패킷들에 대한 계산 결과를 자동으로 판별할 수 있도록 테스트코드를 짜보자
from functools import reduce
packet_string_list = [
'F7 0B 01 19 01 40 10 00 00 B5 EE',
'F7 0B 01 19 02 40 11 01 00 B6 EE',
'F7 0B 01 19 02 40 12 01 00 B5 EE',
'F7 0B 01 19 01 40 20 00 00 85 EE',
'F7 0B 01 19 01 40 30 00 00 95 EE',
'F7 0B 01 19 01 40 40 00 00 E5 EE',
'F7 0B 01 19 01 40 60 00 00 C5 EE',
'F7 0C 01 19 04 40 60 00 02 02 C7 EE',
'F7 0D 01 19 04 40 10 00 01 01 01 B7 EE',
'F7 0B 01 19 04 40 40 00 02 E2 EE',
'F7 0B 01 1F 01 40 60 00 00 C3 EE',
'F7 1C 01 1F 04 40 60 00 61 01 00 00 00 00 00 00 02 62 01 00 00 00 00 00 00 02 D2 EE'
]
def convert(byte_str: str):
return bytearray([int(x, 16) for x in byte_str.split(' ')])
packets = [convert(x)for x in packet_string_list]
def calc(packet: bytearray):
checksum_in_packet = packet[-2]
checksum_calc = reduce(lambda x, y: x ^ y, packet[:-2], 0)
print('checksum_in_packet: %02X, checksum_calc: %02X' % (checksum_in_packet, checksum_calc))
for p in packets:
calc(p)
checksum_in_packet: B5, checksum_calc: B5
checksum_in_packet: B6, checksum_calc: B6
checksum_in_packet: B5, checksum_calc: B5
checksum_in_packet: 85, checksum_calc: 85
checksum_in_packet: 95, checksum_calc: 95
checksum_in_packet: E5, checksum_calc: E5
checksum_in_packet: C5, checksum_calc: C5
checksum_in_packet: C7, checksum_calc: C7
checksum_in_packet: B7, checksum_calc: B7
checksum_in_packet: E2, checksum_calc: E2
checksum_in_packet: C3, checksum_calc: C3
checksum_in_packet: D2, checksum_calc: D2
체크섬 구하는 방법도 알았으니, 이제 조명 관련 패킷 파싱 및 명령 패킷 생성하는 코드를 작성할 준비가 완료됐다!
8. 주의사항
지난 글에서 조명 상태 응답 패킷은 다음과 같은 형태를 가지는 것을 알 수 있었다
[주방 조명 2개 모두 OFF시]
F7 0C 01 19 04 40 60 00 02 02 C7 EE
[주방 1번만 조명 ON시]
F7 0C 01 19 04 40 60 00 01 02 C4 EE
[주방 2번만 조명 ON시]
F7 0C 01 19 04 40 60 00 02 01 C4 EE
[주방 조명 모두 ON시]
F7 0C 01 19 04 40 60 00 01 01 C7 EE
그런데, 상태 명령을 내린 직후 약간 다른 포맷의 응답 패킷이 날아오는 것을 확인할 수 있었다
[주방 1번 조명 ON 명령 직후]
F7 0B 01 19 04 40 61 01 01 C1 EE
[주방 2번 조명 ON 명령 직후]
F7 0B 01 19 04 40 62 02 02 C2 EE
즉, 3~4번째 바이트가 [0x01, 0x19]인 패킷의 7번째 바이트의 상위 4비트는 장소 인덱스이며, 하위 4비트는 디바이스(조명) 인덱스가 된다
이 때, 하위 4비트 디바이스 인덱스가 0일 경우 장착된 모든 디바이스의 상태 정보를 담고 있지만, 인덱스가 0이 아닐 경우 특정 디바이스의 상태 정보만을 담고 있으므로 전체 패킷의 길이가 달라지게 된다!
파서 구현 코딩할 때 반드시 참고해야 한다
(단순히 패킷 길이만으로 판단하면 out of index 예외가 발생하더라...)
9. 정리
조명 관련 RS485 패킷을 해석하는 방법과, 쿼리 패킷 및 On/Off 명령 패킷에 대한 정보를 모두 알아냈다
광교아이파크때와 유사하게, Flask를 사용해서 웹서버를 구축하여 이벤트 루프로 구동하여 홈네트워크 플랫폼(홈킷 + 구글 어시스턴트)와 연동할 수 있도록 구현해나갈 계획
- 어차피 MQTT로 Homebridge 및 Home Assistant랑 연동할 계획이니, 기존 코드를 최대한 재활용
- 코드는 다른 디바이스(아울렛, 환기, 냉/난방 등) 제어를 추가하면서 계속 수정해나갈 계획
※ 아이파크 Bestin때와는 달리 체크섬을 간단하게 구할 수 있어서 너무 좋다 ㅎㅎ
베스틴할때는 체크섬 공식을 끝내 알아내지 못해서 명령 패킷을 모두 모아야하는 번거로움이 있었다..
[시리즈]
힐스테이트 광교산::조명 제어 RS-485 패킷 분석 (1)
힐스테이트 광교산::조명 제어 RS-485 패킷 분석 (2)
'홈네트워크(IoT) > 힐스테이트 광교산' 카테고리의 다른 글
힐스테이트 광교산::아울렛(콘센트) - 애플 홈킷 + 구글 어시스턴트 연동 (0) | 2022.06.14 |
---|---|
힐스테이트 광교산::조명 - 애플 홈킷 + 구글 어시스턴트 연동 (4) | 2022.06.13 |
힐스테이트 광교산::조명 제어 RS-485 패킷 분석 (1) (4) | 2022.06.11 |
힐스테이트 광교산::각방 제어 스위치 뜯어보기 (0) | 2022.06.07 |
힐스테이트 광교산::홈네트워크 월패드 뜯어보기 (4) | 2022.06.05 |