11.5. 연결

central이 광고 스트림에서 peripheral을 선택하고 connect request를 보내면, 양쪽 모두 광고/스캔 모드에서 벗어나 연결 상태로 들어갑니다. 이제 라디오는 링크 계층의 데이터 채널에서 활동을 스케줄링하며, 연결 시점에 합의된 순서에 따라 채널 사이를 의사 난수 방식으로 호핑합니다. 링크 계층 위의 모든 것 – GATT, 보안, L2CAP – 은 여기서 확립된 연결 위에서 동작합니다.

11.5.1. 연결 이벤트

BLE 연결의 두 장치는 연속적으로 스트리밍하지 않습니다. 두 장치는 connection interval에 합의하며, 매 인터벌마다 양쪽이 라디오를 깨워 큐에 쌓인 패킷을 교환하고, 수신한 내용을 확인 응답한 뒤 다시 잠듭니다. 이러한 각 교환을 connection event라고 부릅니다.

위쪽 트랙에 central, 아래쪽 트랙에 peripheral이 있는 타임라인. 각 connection interval 경계마다 두 트랙 모두 "TX/RX"라고 표시된 짧은 펄스를 보여줍니다. 펄스 사이의 간격은 "connection interval"로 표시되며, 각 펄스의 지속 시간은 "connection event"입니다. peripheral에는 "peripheral latency: idle 상태이면 peripheral이 건너뛸 수 있음"으로 표시된 몇몇 건너뛴 이벤트가 있습니다.

각 측의 라디오는 짧은 connection event 동안에만 깨어 있고, 나머지 시간에는 모두 잠들어 있습니다. peripheral은 peripheral latency 하에서 이벤트를 건너뛸 수 있습니다.

이를 제어하는 수치들은 연결 시점에 협상되며 링크 계층이 이를 강제합니다. 이 값들은 요청 측의 조정 항목으로서, 그리고 그 결과로 보고되는 값으로서 애플리케이션에 보입니다.

  • Connection interval. 7.5 ms에서 4 s까지, 1.25 ms 단위입니다. central은 요청이 부당하지 않은 한 peripheral이 요청한 값을 선택합니다. 더 짧은 인터벌은 더 많은 라디오 활동을 대가로 더 낮은 지연 시간으로 데이터를 전달하며, 더 긴 인터벌은 전력을 절약하지만 모든 왕복을 느리게 만듭니다.

  • Peripheral latency. 음이 아닌 정수 N입니다. peripheral은 보낼 것이 없을 때 최대 N개의 connection event를 건너뛸 수 있으며, 빈 교환을 위해 라디오를 깨우는 대신 다시 잠들 수 있습니다. 1초에 한 번 깨어나 보고하지만 드문 즉시 메시지를 위해 낮은 응답성 connection interval을 원하는 센서에 유용합니다.

  • Supervision timeout. 100 ms에서 32 s까지입니다. 어느 한쪽이 이 시간 동안 상대로부터 아무것도 듣지 못하면 링크가 끊어진 것으로 선언되고 양쪽 모두 광고/스캔 상태로 돌아갑니다. 이 타임아웃은 connection_interval * (1 + peripheral_latency) 보다 길어야 하며, 링크 계층은 이를 위반하는 값을 거부합니다.

aioble.Device.connect()min_conn_interval_usmax_conn_interval_us 를 받아 central이 특정 범위를 요청할 수 있도록 합니다. 라디오가 최종적으로 정한 실제 값은 연결이 수립된 후 링크 계층 구성을 통해 다시 읽을 수 있습니다.

11.5.2. 연결 내에서 “central”과 “peripheral”이 의미하는 것

광고 시점에 설정된 역할은 연결이 수립된 후에도 유지됩니다:

  • central은 타이밍을 주도합니다 – 호핑 순서와 connection event의 마스터 측을 소유합니다.

  • peripheral은 반응적입니다. 연결 매개변수의 변경(예를 들어 전력을 절약하기 위한 더 느린 인터벌)을 요청할 수 있지만, 수락 여부는 central이 결정합니다.

이 역할은 누가 GATT 데이터베이스를 호스팅하는지와는 독립적이며, 그것은 별개의 축입니다. 관례상 peripheral이 GATT 서버이기도 하고 central이 GATT 클라이언트이지만, BLE는 어느 쪽이든 GATT 서비스를 호스팅하는 것을 허용합니다. 카메라는 거의 항상 이 관례를 따릅니다: “카메라가 데이터를 게시”하는 애플리케이션에는 peripheral + 서버, “카메라가 센서에서 읽는” 애플리케이션에는 central + 클라이언트입니다.

11.5.3. 최대 전송 단위(MTU)

링크 계층은 기본적으로 짧은 패킷을 전달합니다 – 페이로드 27바이트이며, 그중 GATT에 사용 가능한 것은 23바이트뿐입니다. 이는 작은 측정값이나 짧은 명령에는 충분하지만 멀티바이트 데이터에 비하면 매우 작습니다. 양쪽 모두 이를 라디오 펌웨어가 지원하는 한도(최신 컨트롤러에서는 일반적으로 수백 바이트)까지 상향 협상할 수 있습니다.

aioble API는 aioble.DeviceConnection.exchange_mtu() 를 통해 협상을 수행하며, 그 결과는 mtu 속성에서 사용 가능합니다. 더 큰 MTU는 약 20바이트보다 큰 모든 값에 대해 더 적은 왕복을 의미하며, 그 대가는 약간의 버퍼 메모리입니다.

11.5.4. 수명

연결은 다음 중 하나가 발생할 때까지 지속됩니다:

  • 어느 한쪽이 disconnect() 를 호출하거나,

  • supervision timeout이 발동하거나(범위 이탈, 라디오 꺼짐, 피어 충돌), 또는

  • 명시적인 링크 계층 실패(암호화 불일치, 페어링 거부)가 발생하는 경우입니다.

연결이 끊어지면, 그 위에서 큐에 쌓였거나 진행 중이던 모든 GATT 작업은 aioble.DeviceDisconnectedError 를 발생시키며, 애플리케이션이 들어가 있는 모든 async with connection 블록은 정상적으로 종료됩니다. peripheral은 일반적으로 aioble.advertise() 로 돌아가 다음 central을 기다리며 응답하고, central은 다시 스캔하거나 연결 해제를 애플리케이션에 노출하여 응답합니다.

이 수명은 aioble 이 유용한 이유 중 하나입니다. 동기식 BLE API는 연결 해제 콜백과 이벤트 마스크를 노출해야 하지만, asyncio 버전은 연결 해제를 작업을 기다리고 있던 코루틴 내부의 예외로 변환하며, 이는 바로 async with 정리가 만들어진 목적입니다.