10.6 다양한 물리 계층 및 네트워크 인터페이스 지원

10.6 다양한 물리 계층 및 네트워크 인터페이스 지원

인터넷(TCP/IP)만이 통신망의 전부라고 생각하는가?
땅속 10미터 아래에 박힌 진동 센서, 해저 케이블로 연결된 심해 탐사정, 혹은 수백 달러짜리 셀룰러 모뎀을 달 여력이 안 되어 값싼 블루투스(BLE) 모듈만 덩그러니 달고 있는 1달러짜리 스마트 태그.

진정한 만물인터넷(IoT) 프로토콜은 기가비트 이더넷(Gigabit Ethernet)부터 수 킬로미터를 겨우 날아가는 로라(LoRa)망에 이르기까지, 인간이 만든 모든 ‘주파수’ 와 ‘구리선’ 위를 아무런 수정 없이 흘러 다녀야 한다.
이 장은 Zenoh-Pico가 어떻게 TCP/IP 라는 현대적 우산을 집어 던지고, 가장 원시적이고 더러운 시리얼 라인과 초저전력 무선망 위로 과감히 다이빙하는지를 보여주는 인프라 브리징(Bridging) 런북이다.

1. LwIP 스택을 활용한 이더넷(Ethernet) 및 Wi-Fi 통신

LwIP(Lightweight IP)는 RAM수 KB 만으로도 완벽한 TCP/UDP 소켓 환경을 흉내 내주는 임베디드계의 전설적인 오픈소스 스택이다. RTOS(FreeRTOS 등)를 올리는 MCU의 99%는 이 LwIP 를 기반으로 인터넷에 접속한다.

1.0.1 [Runbook] LwIP TCP 버퍼 스매싱(Buffer Smashing) 방어 전술

LwIP 소켓을 물고 있는 Zenoh-Pico가 가장 쉽게 터지는 경우는 대용량의 패킷을 보낼 때다. z_publisher_put 은 패킷을 쪼개지 않는다!

1. LwIP PBUF_POOL 튜닝 (lwipopts.h)
LwIP가 한 번에 다룰 수 있는 패킷 덩어리(PBUF)의 수를 당신의 Zenoh 통신 크기에 맞춰 강제로 늘려야 한다.

// 1500바이트(MTU) 짜리 택배 상자를 넉넉히 20개 준비한다
#define PBUF_POOL_SIZE 20
#define PBUF_POOL_BUFSIZE 1500
// TCP 송신 윈도우 크기를 8KB 로 부스트!
#define TCP_SND_BUF 8192

2. UDP 로 쏠 것인가, TCP 로 쏠 것인가?

  • 로봇 팔의 실시간 제어명령(10ms 주기): LwIP 의 UDP 소켓 연동. 중간에 데이터가 유실되더라도 LwIP 가 재전송한답시고 CPU를 잡아먹지 않게 한다. 네트워크가 막히면 과감히 옛날 명령은 버리고(Drop) 가장 최신 명령만 수신하게 해야 로봇이 미쳐 날뛰지 않는다.
  • 카메라 이미지 / 로그(Log) 파일: LwIP 의 TCP 소켓 연동. Zenoh 단에서 신뢰성을 보장해야 하므로 Reliable 옵션으로 개통해야 한다. 단, 칩에 열이 펄펄 날 것이다.

2. 시리얼 통신(UART, USB-CDC) 기반의 Zenoh 시리얼 연동

WiFi 모듈이 고장 나거나 인터넷을 연결할 수 없는 공장 지하 깊은 곳에 있는 센서는 어떻게 해야 할까?
RS-232C 나 USB 케이블 하나를 꼽아 라즈베리 파이(Zenoh Router)와 직접 1:1 로 연결하는 방식이 가장 무식하지만 절대 끊기지 않는 방법이다. Pico는 TCP 없이도 이 시리얼 선 하나를 타고 전 세계 통신망에 합류할 수 있다.

2.0.1 [Runbook] UART 하이재킹(Hijacking) 시리얼 브릿지 전술

1. MCU (Zenoh-Pico) 측 세팅
Pico 의 Config 설정에서 타겟을 TCP IP가 아니라 시리얼(Serial) 스킴으로 강제 세팅한다.

z_config_t config = z_config_default();
// "나는 특정 시리얼 드라이버 위에서 무릎을 꿇겠다" 는 선언
zp_config_insert_json(&config, ZP_PEER_KEY, "\"serial/\""); 
// 커스텀 포팅 함수를 바인딩 (10.3.4장 참조)

이제 MCU가 쏘는 모든 Zenoh 패킷은 IP 구조 없이 순수 바이트 덩어리 배열로 UART 의 TX 핀을 타고 쏟아져 내리기 시작한다.

2. 중간 다리 라우터 (라즈베리 파이 Zenoh-Rust) 세팅
PC나 라즈베리 파이에 깔린 실제 Zenoh 라우터가, 이 USB 시리얼 포트를 “마치 이더넷 케이블인 것처럼” 낚아채서 클라우드와 이어줘야 한다.

## 라우터를 기동할 때 시리얼 포트(/dev/ttyUSB0)를 수신 포트로 지정한다.
## 보드레이트(Baudrate) 115200 으로 강제 매칭!
zenohd -l serial//dev/ttyUSB0#baudrate=115200

이 쉘 명령어가 발동되는 순간 마법이 일어난다. 라즈베리 파이의 라우터가 USB포트로 빨려 들어오는 쓰레기 같은 UART 바이트들을 모조리 조립하여 완벽한 글로벌 Zenoh 패킷으로 부활시킨 뒤 당신의 쿠버네티스 클라우드로 PUT 해 버린다!

3. BLE(Bluetooth Low Energy)를 통한 데이터 브릿징

“와이파이는 배터리를 너무 처먹고, 시리얼 선은 움직이는 사람 손목에 꽂을 수가 없잖아요!”
그렇다. 스마트 워치나 부착형 의료 센서는 오직 전력 소모의 구두쇠인 **저전력 블루투스(BLE, Bluetooth Low Energy)**로만 연명한다.

거대한 TCP 프로토콜조차 거부하는 이 쪼잔한 BLE 규격 안으로, 과연 우리의 Zenoh-Pico가 침투할 수 있을까? 물론이다! 그 열쇠는 가장 폭력적인 **“특성 덮어쓰기(GATT Characteristic Overloading)”**에 있다!

[1. GATT 속성 지배: BLE를 시리얼 파이프라인처럼 쓰기!]

BLE에는 소켓통신 같은 개념이 없고, 오직 “변수값을 읽고 쓰는(GATT)” 개념만 존재한다.
여기서 자비심 없는 임베디드 코더들은 BLE의 특성치(Characteristic)를 변수가 아니라 거대한 **통풍구(Rx/Tx Pipe)**로 개조해버린다! (흔히 SPP-over-BLE 방식이라 불린다).

// z_hal_io.c 내부 
// Pico가 쏘고 싶은 데이터(Payload) 뭉치를 BLE Notification 으로 강제 발사!
int16_t z_hal_io_write(z_io_t *io, const uint8_t *buf, uint16_t len) {
    // 20바이트씩(혹은 MTU 사이즈에 맞춰서) 잘라서 BLE 핸드폰 앱으로 난사한다!!
    ble_gatts_send_notify(ble_conn_id, ZENOH_TX_CHAR_HANDLE, len, buf);
    return len;
}

[2. 스마트폰 안의 중계 라우터(Gateway App)]

이 바이트들을 뱉는 피트니스 밴드(Pico)가 혼자 클라우드로 날아갈 순 없다.
이를 위해 사용자의 주머니 속에 있는 **스마트폰(Android/iOS)**에서 백그라운드 앱이 돌아가야 한다. 스마트폰은 BLE로 저 바이트 뭉치를 받아 파싱(Parsing)한 뒤, 자신의 막강한 5G/LTE 라우팅 기능을 활용하여 진짜 클라우드망에 쏘아 올린다!!

전력망이 닿지 않는 외딴 섬의 심박수 센서가 이 얇은 BLE 실가닥 하나를 타고 5G망을 거쳐 지구 반대편의 웹브라우저 차트에 실시간 렌더링(Pub/Sub)되는 광경! 이것이 Zenoh 생태계가 빚어내는 무시무시한 초토화 브릿징 기술이다!

4. BLE(Bluetooth Low Energy)를 통한 데이터 브릿징

움직이는 로봇 10대가 있다. 서로 Wi-Fi로 묶이지 못하는 환경이라면, BLE 를 통해 스마트폰(Gateway)과 연결하고 그 스마트폰이 클라우드의 Zenoh 네트워크로 대리 송출(Relay)하도록 만들어야 한다.

BLE는 MTU(Maximum Transmission Unit)가 겨우 20바이트 ~ 244바이트 수준인 극악의 병목 구간이다. 일반적인 Zenoh 패킷 헤더만 붙여도 이 크기를 넘어가 패킷이 산산조각 난다.

4.0.1 [Runbook] BLE 파편화(Fragmentation) 극복 게이트웨이 전술

1. Zenoh-Pico 의 BLE 커스텀 청크 송수신 (MCU)
MCU 안에 있는 BLE 스택 (예: nRF52 SoftDevice) 의 Notification 기능(TX)에 Pico의 write 함수를 매핑하되, 바이트가 쪼개지는(Truncated) 현상에 대비해 버퍼링 루틴을 짜야 한다.

// [커스텀 네트워크 포팅]
int z_system_write(int fd, const uint8_t *buf, size_t count) {
    size_t sent = 0;
    while(sent < count) {
        size_t chunk = (count - sent > 240) ? 240 : (count - sent);
        // BLE 로 240바이트 씩 쪼개서 발송
        ble_nus_data_send(&m_nus, (uint8_t*)buf + sent, &chunk, m_conn_handle);
        sent += chunk;
    }
    return sent;
}

2. 게이트웨이(스마트폰/태블릿) 단말의 조립기 (Android/iOS)
이 스마트폰에는 네이티브 Zenoh 라이브러리 (Rust 또는 Kotlin 브릿지)가 탑재되어 있어야 한다.
-스마트폰 앱은 BLE(GATT) 로부터 240바이트 조각들을 미친듯이 수신받는다.
-스마트폰 앱 내부 메모리 버퍼에서 조각들을 풀(Full) 바이트 배열로 조립한다.
-조립이 끝나면 드디어 모바일 네트워크(5G/LTE)를 타고 zenoh.Put( "robot/01/sensor", payload ) 를 때린다.

이 패턴은 웨어러블(기어, 워치) 장비나 헬스케어 동인(Sensor) 장치에서 데이터를 안전하게 AWS/GCP 백엔드로 끌고 올라오는 현존 최고의 “모바일 에지 게이트웨이” 방식이다.

5. IEEE 802.15.4, LoRa 등 저전력 광역 통신망(LPWAN) 적용 시 고려사항

산불 감지 센서나 농장의 스마트 팜 센서들은 LoRa 칩을 달고 몇십 킬로미터(km)를 날아가야 한다.
여기는 핑(Ping)이 2초가 넘고 초당 전송률이 몇십 바이트에 불과한, 텔레넷(Telnet) 시절보다 느린 “시간과 공간의 방” 이다.

5.0.1 [Runbook] LPWAN 대역폭 질식사 방어 전술

이 환경에 TCP 프로토콜을 태우는 것은 자살행위다. 반드시 UDP 기반으로 가되, Pico 엔진이 대답(Acknowledge)을 기다리다가 타임아웃되어 뻗어버리는 현상을 막아야 한다.

1. Reliability(신뢰성) 옵션의 완전한 폐기
LoRa 망이나 Zigbee 网에서 패킷이 유실됐다고 재전송(Retransmit)을 요구하면 센서 배터리가 다 닳아버린다.
무조건 순수 베스트 에포트(Best-Effort, 유실되면 그만인) 방식으로 던지기만 해야 한다. (QoS=0).

z_publisher_options_t pub_opts = z_publisher_options_default();
// 신뢰성 등 기능은 1바이트의 헤더조차 낭비이므로 꺼버린다!
pub_opts.reliability = Z_RELIABILITY_BEST_EFFORT;

2. 세션 Liveliness 주기의 극한 이완 (Timeout 확충)
라우터는 1초라도 응답이 없으면 연결된 센서를 “죽었다” 고 판별해버린다. 하지만 LoRa 환경에선 1초가 아니라 1분을 넘길 수도 있다.
Pico Config 및 Router Config에서 LeaseKeepAlive 주기를 극단적으로 늘려(예: 600초) 라우터가 인내심을 갖고 당신의 패킷을 몇 시간이고 기다려 주게끔 포용성(Tolerance) 세팅을 하는 것이 생존의 필수 조건이다.