Firmware Driver Boundary

이 문서는 실제 PCB/sensor driver 팀이 dev-guide의 C/Firmware 예제를 참고할 때 봐야 하는 driver boundary 기준입니다. 현재 예제는 실제 board driver를 포함하지 않고 deterministic synthetic sensor를 사용합니다. 목적은 hardware 없이도 PatientSignalSample, packet quality, firmware artifact 흐름을 검증할 수 있게 하는 것입니다.

1. 목적

Firmware driver는 clinical risk를 판단하지 않습니다. Driver는 sensor에서 관찰한 값을 잃지 않고 C core가 이해할 수 있는 sample과 quality evidence로 변환합니다.

Driver boundary가 지켜야 하는 질문은 아래입니다.

  • sample timestamp와 sequence가 단조롭게 증가하는가
  • raw channel, vital estimate, motion, contact quality가 같은 sample 시점의 evidence인가
  • sensor dropout, clipping, saturation, excessive motion을 backend가 해석할 수 있는 flag로 보존하는가
  • low SpO2처럼 유효한 생체신호와 sensor disconnected처럼 무효한 측정을 구분하는가
  • firmware version, hardware revision, calibration id가 packet에 남는가

2. Core contract

Board driver가 최종적으로 채워야 하는 core input은 PatientSignalSample입니다.

typedef struct PatientSignalSample {
  unsigned long long timestamp_ms;
  unsigned int sequence_number;
  int ppg_raw;
  int ecg_raw;
  unsigned int spo2_permille;
  unsigned int heart_rate_bpm;
  unsigned int motion_mg;
  unsigned int contact_quality;
} PatientSignalSample;

이 struct는 hardware register shape가 아닙니다. Driver adapter가 sensor register, DMA buffer, BLE packet, AFE output을 이 domain sample로 변환합니다. patient-signal-c-core는 ADC bus, RTOS queue, DMA, BLE stack을 몰라야 합니다.

2-1. Field mapping

Field Driver responsibility
timestamp_ms publish time이 아니라 sample acquisition time을 기록합니다. Wall-clock이 없으면 firmware monotonic tick을 millisecond로 변환하고 기준 epoch를 release note나 driver 문서에 남깁니다.
sequence_number sample source별로 단조 증가합니다. wrap이 가능하면 wrap width와 wrap 이후 gap 계산 정책을 문서화합니다. Gap을 adapter에서 조용히 메우지 않습니다.
ppg_raw, ecg_raw clipped/saturated 판단이 가능하도록 ADC/AFE native scale을 보존합니다. scale 변환이 필요하면 gain, offset, unit, rail 값을 calibration metadata나 driver 문서에 남깁니다.
spo2_permille 0.1% 단위 SpO2 estimate, 예: 97.2% -> 972
heart_rate_bpm 현재 sample/window에 대응하는 heart rate estimate
motion_mg motion artifact 판단에 사용할 acceleration magnitude
contact_quality 0-100 범위의 contact/sensor confidence

Driver가 값을 smoothing하거나 보정할 수는 있지만, 품질 판단에 필요한 원인을 숨기면 안 됩니다. 예를 들어 contact loss를 단순히 마지막 정상 값으로 채우면 backend는 missingness와 artifact를 구분할 수 없습니다.

Driver가 여러 sensor clock을 합쳐 하나의 sample을 만들 때는 timestamp_ms가 어떤 channel 기준인지 명시합니다. PPG, ECG, motion sample rate가 다르면 adapter는 resampling rule을 소유할 수 있지만, resampling으로 생긴 missing value와 stale value를 quality evidence에서 숨기지 않습니다.

2-2. Packet quality mapping

C core는 sample과 sequence를 보고 아래 packet quality를 계산합니다.

typedef struct PatientSignalPacketQuality {
  unsigned int dropped_samples;
  bool sensor_disconnected;
  bool clipped;
  bool saturated;
  bool excessive_motion;
} PatientSignalPacketQuality;

Driver가 지켜야 하는 mapping 기준은 아래입니다.

Evidence Expected output direction
sequence gap or expected count gap dropped_samples > 0
contact quality near zero or all vital/raw values zero sensor_disconnected = true
negative raw channel or raw channel above ADC range clipped = true
SpO2 above physiological max or raw near upper rail saturated = true
high motion magnitude excessive_motion = true

Low SpO2는 자동으로 invalid가 아닙니다. Low SpO2가 유효한 measurement라면 backend risk workflow가 clinical risk를 판단해야 합니다. Driver와 C core는 측정이 유효한지, 품질 근거가 무엇인지 제공하는 데 집중합니다.

2-3. Invalid, degraded, clinical concern 구분

Driver boundary에서 가장 중요한 구분은 clinical concern과 measurement integrity입니다.

State Meaning Driver behavior
valid clinical concern 측정은 유효하지만 값이 임상적으로 우려됨 값을 보존하고 quality flag를 과장하지 않습니다. 예: low SpO2, high heart rate
degraded measurement 측정은 가능하지만 artifact 가능성이 큼 sample은 전달하되 excessive_motion, low contact_quality, clipping evidence를 보존합니다.
invalid measurement 측정 자체를 신뢰할 수 없음 disconnected/dropout evidence를 남기고 stale value나 last-known-good value로 조용히 대체하지 않습니다.

이 구분이 깨지면 downstream 문제가 생깁니다. Low SpO2를 sensor_disconnected처럼 처리하면 risk workflow가 실제 임상 우려를 놓칠 수 있고, sensor dropout을 정상 SpO2로 채우면 dashboard가 오래되거나 무효한 신호를 현재 상태처럼 보여줄 수 있습니다.

3. Adapter boundary

현재 bare-metal style app은 아래 위치에 synthetic driver를 둡니다.

apps/wearable-firmware/src/adapters/outbound/mock_sensor.c
apps/wearable-zephyr-firmware/src/adapters/outbound/mock_sensor.c

실제 board driver를 붙일 때는 같은 boundary에 board-specific adapter를 추가합니다.

apps/wearable-firmware/src/adapters/outbound/afe_sensor.c
apps/wearable-firmware/src/adapters/outbound/afe_sensor.h

권장 adapter 흐름은 아래입니다.

sensor register / DMA / BLE payload
  -> board-specific driver
  -> PatientSignalSample
  -> patient_signal_build_packet_from_sample
  -> PatientSignalPacket
  -> gateway/transport adapter

Transport는 별도 adapter입니다. MQTT, BLE, UART, file dump, gateway HTTP는 packet을 전달하는 방법이지, sample quality rule을 소유하지 않습니다.

Board-specific adapter는 아래 정보를 최소한으로 드러내야 합니다.

  • sensor/AFE model과 driver version
  • sample rate와 channel alignment policy
  • raw channel scale, gain, offset, rail range
  • timestamp source와 clock drift 가정
  • sequence wrap width와 overflow policy
  • calibration id와 calibration 적용 위치

이 정보는 C core API로 모두 밀어 넣지 않습니다. Core API에는 sample과 metadata만 전달하고, driver-specific 설명은 README, release manifest, calibration metadata, test evidence에 남깁니다.

4. Timing and buffering

1단계 예제는 fixed-size window와 single-sample packet build로 boundary를 보여줍니다. 실제 board에서는 아래 항목을 board adapter에서 다룹니다.

  • ISR-safe ring buffer
  • DMA half/full transfer callback
  • monotonic clock source
  • sample period drift
  • sequence wrap policy
  • packet flush interval
  • backpressure or queue overflow

이 항목은 C core 안으로 들어가지 않습니다. Core는 이미 만들어진 PatientSignalWindowPatientSignalSample을 받아 summary와 packet quality를 계산합니다.

4-1. Overflow and backpressure

실제 board에서는 queue overflow가 발생할 수 있습니다. 이때 driver는 아래 원칙을 따릅니다.

  • overflow count를 내부 metric이나 event로 남깁니다.
  • dropped sample은 sequence gap 또는 expected_sample_count gap으로 packet quality에 드러나야 합니다.
  • ISR이나 DMA callback에서 clinical 판단을 하지 않습니다.
  • backpressure 때문에 sampling을 멈췄다면 packet flush 시점과 skipped sample 범위를 review evidence에 남깁니다.
  • stale sample을 새 timestamp로 다시 발행하지 않습니다.

Host simulation과 native_sim은 실제 ISR timing을 완전히 재현하지 못합니다. 대신 overflow와 gap을 deterministic scenario로 만들어 packet quality가 변하는지 검증합니다.

5. Test evidence

Driver boundary를 바꾸는 PR은 아래 evidence를 남겨야 합니다.

Evidence Purpose
host unit test sample validity, quality flag, sequence gap rule
host integration test synthetic scenario -> expected packet quality
bare-metal host simulation app bootstrap과 packet build wiring
Zephyr native_sim smoke RTOS queue/thread composition
firmware release manifest .elf/.hex/.bin/.map checksum and source revision

실제 PCB/HIL이 필요해지면 별도 runner와 workflow로 분리합니다. HIL 실패가 host C package, Python API, TypeScript UI CI를 막지 않게 하기 위해서입니다.

Review description에는 아래 evidence block을 남깁니다.

Firmware driver evidence
- timestamp source:
- sequence policy:
- sample rate/channel alignment:
- raw scale/calibration:
- quality scenarios tested:
- host tests:
- native_sim result:
- artifact manifest:
- known limitations:

이 block은 regulatory 문서가 아니라 engineering review index입니다. Reviewer가 driver change와 packet quality change를 빠르게 연결해 볼 수 있게 하는 목적입니다.

6. Review checklist

Firmware driver review는 아래를 확인합니다.

  • driver가 PatientSignalSample 외부로 clinical risk 용어를 만들지 않습니다.
  • timestamp와 sequence policy가 문서화되어 있습니다.
  • dropout, clipping, saturation, excessive motion을 구분할 수 있는 evidence가 보존됩니다.
  • calibration id, firmware version, hardware revision이 PatientSignalDeviceMetadata로 전달됩니다.
  • packet quality 변화가 Rust/Python preprocessing fixture와 충돌하지 않습니다.
  • board-specific code가 patient-signal-c-core에 들어가지 않습니다.
  • queue overflow, stale sample, sequence wrap, clock drift에 대한 판단이 adapter 문서나 test evidence에 남아 있습니다.
  • low SpO2 같은 valid clinical concern을 invalid measurement로 바꾸지 않습니다.
  • sensor disconnected 같은 invalid measurement를 last-known-good value로 숨기지 않습니다.

이 기준을 지키면 실제 PCB driver가 붙어도 backend contract, Rust preprocessing, Python risk workflow, TypeScript monitoring UI가 같은 signal quality language를 유지할 수 있습니다.