12.1. 프로토콜 라이브러리가 필요한 이유¶
카메라에서 호스트 PC로 바이트를 전송하는 데는 케이블 한 쌍과 보드 레이트만 있으면 충분합니다. USB-CDC와 UART 모두 카메라 프로그램에 스트림을 제공하는데, write 가 한쪽 끝으로 바이트를 넣고 read 가 다른 쪽 끝에서 바이트를 꺼냅니다. 그렇다면 프로토콜 라이브러리 는 그 위에 무엇을 더해 줄까요?
원시 바이트 위에 진지한 카메라-호스트 채널을 직접 구축하려고 하면 매번 직접 작성해야 하는 세 가지가 있습니다:
프레이밍(framing). 바이트 스트림에는 본래 구조가 없습니다. 카메라가 temp=42 를 쓰면 호스트는 temp= 를 읽고, 그 다음 인터럽트가 나중에 도착하며, 이어서 42 와 그 뒤에 이미 흘러들어온 humid=... 로 시작하는 다음 메시지를 읽게 됩니다. 바이트에는 경계가 없습니다. 사소하지 않은 모든 호스트 링크는 결국 어떤 마커를 고안하게 됩니다 – 메시지 사이의 \n, 길이 접두사 헤더, 이진 페이로드를 위한 이스케이프 시퀀스 – 그래야 수신 측이 한 메시지가 어디서 끝나고 다음 메시지가 어디서 시작하는지 알 수 있습니다. 프로토콜 라이브러리는 동기화 워드(sync word)와 길이 필드를 갖춘 통일된 패킷 형식을 제공하여 수신 측이 결코 추측할 필요가 없게 합니다.
신뢰성(reliability). USB-CDC는 정상 동작 시 바이트를 조용히 누락시키지 않지만 UART는 그렇습니다(호스트가 포트를 충분히 빠르게 처리하지 못할 때). 그리고 직렬 케이블을 뽑았다가 다시 꽂으면 한쪽에 부분적인 패킷이 남을 수 있습니다. 올바른 대응은 손상을 감지하고, 상대 측에 재전송을 요청하며, 온전하게 도착한 메시지만 애플리케이션 코드에 전달하는 것입니다. 프로토콜 라이브러리는 CRC와 패킷별 확인 응답(acknowledgement)으로 모든 패킷에 대해 이를 수행합니다 – 기본적으로 켜져 있으며, 애플리케이션은 재시도를 보지 못합니다.
멀티플렉싱(multiplexing). 카메라와 호스트 사이에는 USB-CDC 포트가 정확히 하나뿐입니다. 카메라가 이미지를 스트리밍하는 동시에 호스트가 설정을 보내고 또한 IDE가 print 출력을 위해 stdout 을 읽는다면, 이 세 가지 교환은 모두 그 단일 바이트 스트림을 공유해야 합니다. 프로토콜 라이브러리는 각 독립 스트림에 채널 번호를 부여하고, 카메라가 각각에 대해 Python 클래스를 등록하도록 하며, 각 채널에 대한 호스트의 읽기가 서로 간섭하지 않도록 합니다. 애플리케이션 코드 관점에서 각 채널은 자신만의 전용 링크처럼 보입니다.
12.1.1. 직접 작성하지 않는 이유¶
직접 작성할 수도 있습니다. 직렬 회선에서 세 가지를 모두 올바르게 동작시키려면 몇 주가 걸리고, 프레이밍이 핫플러그 복구를 깔끔하게 처리하고, 재전송이 왕복에 에너지를 낭비하지 않고 동작하며, 멀티플렉서가 한 채널의 바이트를 다른 채널로 손상시키지 않으면서 부분 읽기를 견뎌내게 하려면 다시 몇 주가 더 걸립니다.
프로토콜 라이브러리는 이미 그 작업을 완료했고, 지원되는 모든 카메라에서 검증되었으며, 동일한 와이어 형식을 사용하는 호스트 측 라이브러리가 함께 제공됩니다. 이를 사용하면 카메라 측 코드는 채널당 작은 클래스 하나가 되고, 호스트 측 코드는 channel_read 와 channel_write 메서드를 갖춘 Camera 객체가 됩니다. 절약된 정신적 여유는 애플리케이션이 실제로 하는 일에 쓰입니다.
이 장은 프로토콜을 밑바닥부터 가르칩니다: 와이어 형식, 프레이밍 규칙, 신뢰성 메커니즘, 채널 모델, 그리고 마지막으로 양쪽 끝의 Python 클래스까지 다룹니다. 마지막에 이르면 독자는 카메라와 통신하는 호스트 GUI, 카메라에서 노트북으로 센서 데이터를 스트리밍하는 스크립트, 그리고 openmv-projects/tools/ 에 포함된 것과 같은 대화형 보정 도구를 만들 수 있습니다.