mbb 응답(rbb:→reb:×6→raa:)에서 rbb: 헤더가 piezoCollector를
혼동시켜 그래프가 안 뜨는 문제 발생.
- Spot 단일 측정: sendChannelsOnly(maa) 사용
- 자동 측정 10초째: measure()→maa 사용 (mbb는 파싱 검증 후 전환)
- IMU 전송 시 isMeasuring 체크 추가 (측정 중 충돌 방지)
- sendChannelsOnly() 함수 추가 (기존 sendAllChannels와 동일)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
기기에서 rxs:mid? (unknown command) 응답 → 포맷 문제 가능성
buildCommand("mid") → buildCommandASCII("mid", " ") 로 변경하여 테스트
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. ADC 로그: 측정 완료 시 MeasurementLogService.logMeasurement() 호출
→ 6채널 raw ADC + 채널별 ant/post/score 저장 (JSONL)
2. 기기 정보: 연결 직후 mid? 전송 → rid: 응답 파싱 → 로그 기록
(HW Version, Serial Number, FW Version 일괄 조회)
3. LED 모드: Placement Guide 연동
- 스캔 시작 → mls?(5) ALIGN_SEARCHING
- PASS → mls?(6) ALIGN_COMPLETE
- 스캔 종료 → mls?(0) OFF
4. Reconnect: 3/5/10초 5회 → 3초 간격 무한 재시도
(연결됐다가 끊어진 경우, 사용자가 Home 돌아갈 때까지)
5. BLE 로그: mid/mls TX 파싱, rid:/rls: RX 파싱 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Low echo: VALLEY_STOP_RISE=50, EDGE_DIST_DECAY=0.12, edge boundary, score 공식
- Placement: CV threshold 방식 전체 설명 (CV 계산, SI/LR 판정, severity, relaxation)
- 기존 urineLen score 방식 → CV threshold 방식으로 전면 교체
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TX: "maa 6ch measure", "msn battery query", "mpa piezo power ON" 등
RX: "rsn battery=3850mV (100%)", "reb 100samples", "raa all-ch complete",
"rpa piezo ack", "rag gain calibration (24 values)" 등
펌웨어팀에 보낼 때 raw hex 없이도 통신 흐름을 이해할 수 있음
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WATCHDOG_STARTED/TIMEOUT/GATT_ERROR/RECONNECT 등 이벤트 로그는 유지.
5초마다 찍히던 WATCHDOG_CHECK/THREAD 상태 로그 제거.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
근본 원인: Android BLE가 disconnect 시 STATUS_DISCONNECTED가 아닌
GATT 에러(status≠0)로 콜백을 보냄. 기존 에러 경로에서는
reconnectAttempt>0일 때만 재연결 → 첫 끊김 시 재연결 안 됨.
수정:
- wasConnected 체크: 이전에 연결됨 + 사용자 disconnect 아님 → 자동 reconnect
- GATT_ERROR 로그 + DISCONNECTED(gatt_error) 로그 기록
- stopBatteryPolling/stopWatchdog 호출 추가
- watchdog: connected=false 감지 시 무한 루프 대신 break
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
watchdog이 실제로 돌고 있는지, 어디서 멈추는지 진단용.
매 5초: WATCHDOG_CHECK silence=Xms connected=true/false
interrupt 시: WATCHDOG_THREAD interrupted
flag 종료 시: WATCHDOG_THREAD stopped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
기존: handler.post 안에서 로그+disconnect → handler 블로킹 시 둘 다 안 됨
변경:
- debugLogger.error() → watchdog Thread에서 직접 호출 (CopyOnWriteArrayList 스레드 안전)
- gatt.disconnect()/close() → watchdog Thread에서 직접 호출 (GATT는 any thread OK)
- UI 업데이트 + reconnect만 handler.post로 전달
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CCCD 없는 경로(no CCCD, no RX)에서도 startWatchdog() + startBatteryPolling() 호출
- watchdog 시작 시 WATCHDOG_STARTED 로그 기록 (실제 동작 확인용)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
기존: handler.postDelayed로 watchdog 실행 → BLE 스택이 handler를
블로킹하면 watchdog도 멈춤 (좀비 상태 감지 불가)
변경: 별도 daemon Thread에서 5초마다 lastRxTimestamp 체크
→ BLE handler가 블로킹되어도 독립적으로 15초 타임아웃 감지
→ 감지 시 handler.post로 메인 스레드에서 forceDisconnectAndReconnect 실행
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
기존: connectGatt(autoConnect=true) — Android에서 매우 불안정, 수분 걸림
변경: BLE 스캔으로 기기를 다시 찾은 후 connectGatt(autoConnect=false)로 빠르게 연결
재연결 흐름:
1. watchdog 15초 타임아웃 → forceDisconnectAndReconnect()
2. scheduleAutoReconnect() → 3s/5s/10s 백오프
3. attemptAutoReconnect():
- BLE 스캔 시작 (기기 이름 or MAC 주소 매칭)
- 발견 시 즉시 connectGatt(autoConnect=false) → 빠른 연결
- 8초 스캔 타임아웃 → 다음 attempt
- 최대 5회 시도 후 실패 처리
4. 모든 단계 BleDebugLogger에 기록
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Android BLE가 disconnect 콜백을 안 보내는 경우(좀비 연결) 대응:
- 마지막 RX로부터 15초간 응답 없으면 강제 disconnect → auto reconnect
- processReceivedData()에서 lastRxTimestamp 갱신
- 5초마다 watchdog 체크
- WATCHDOG_TIMEOUT 이벤트 BleDebugLogger에 기록
- 수동 disconnect 시 watchdog 해제
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- exportToDownloads(): Downloads/VesiScan_BLE_날짜.log 로 저장
- 실패 시 fallback으로 내부 저장소에 저장
- Toast로 저장 경로 안내
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- center 채널 3개 미만이면 BV 계산 스킵 (신뢰도 부족 방지)
- 1~2채널: "측정 부족 (N/4채널) — 센서 위치를 조절해주세요"
- 0채널: "방광이 감지되지 않았습니다. 센서 접촉을 확인해주세요."
- BleDebugLogger에 BV_SKIP/BV_FAIL 기록
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 수동 1회 → 자동 반복 스캔 (Start/Stop 토글)
- 3초마다 maa 전송 → Score 실시간 갱신
- 사용자가 센서 움직이면서 점수 변화를 즉시 확인 가능
- "Scanning every 3s — move sensor and watch the score" 안내 문구
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- BleManager: 배터리 폴링 30초→5초 (펌웨어 15초 타임아웃 방지, iOS 동일)
- BleManager: 자동 reconnect with 백오프 (3s→5s→10s, 최대 5회)
- scheduleAutoReconnect() + attemptAutoReconnect()
- GATT 에러 시에도 백오프 재시도
- 수동 disconnect 시 자동 reconnect 취소
- PiezoMonitoringView: 끊김 시 즉시 파란 배너(RECONNECTING)
- 성공 시 배너 자동 숨김 + 배터리 폴링 재시작
- 5회 실패 시 빨간 배너 + 수동 Reconnect 버튼
- 측정 중 끊김 → isMeasuring 리셋 + 자동측정 중지
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CRITICAL: In multi-channel mode, "ree:" packets between channels
were silently dropped instead of saving the current channel data.
This caused channel data loss when channels are separated by "ree:".
Fixed: Added missing isMultiChannel + ree: branch that calls
saveCurrentChannel() — matching Swift behavior exactly.
Found via line-by-line Swift vs Kotlin verification.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GreenZoneConstants.kt — all 16 constants (thresholds, physical)
UrinAI.kt — urine gatekeeper (binary threshold detection)
PiezoEchoAnalyzer.kt — low-echo detection with prominence-based wall selection
PiezoBVEstimator.kt — Frustum BV estimation with device presets
1:1 port from Swift, same logic, same constants.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded Korean UI strings in Piezo, CatheterizeSheet,
VoidingDiary, and UrineCameraScreen with stringResource() calls.
Add values/strings.xml (English) and values-ko/strings.xml (Korean).
System language setting now controls app language.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
A. Reminder: ReminderService (AlarmManager chain), ReminderSettingsView,
bell icon in Piezo top bar, reschedule on catheterization
B. Consent: ConsentView (3 mandatory items), onboarding→consent→registration flow,
persistent consent storage, sex field in User + Registration Step 2
C. Diary: 24hUV, NUV, NPI, max capacity, day/night frequency metrics card
D. Design: DesignTokens.kt, 48dp touch targets, DonutChart semantics, pressScale modifier
E. Quality: BuildConfig.DEBUG log wrapping, DateFormatter caching, background I/O for diary store
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Camera was hidden behind PiezoMonitoringView because it was rendered
as a child composable. Now uses AppScreen.URINE_CAMERA for full
Crossfade navigation — camera gets its own screen transition.
Flow: Catheterize → 카메라 → AppScreen.URINE_CAMERA → measure → back to PIEZO_MONITORING
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace ModalBottomSheet-based flow with AlertDialog:
- Catheterize → AlertDialog with 3 buttons (수기/음성/카메라)
- 수기: AlertDialog with number TextField + save
- 음성: AlertDialog with auto-start SpeechRecognizer + auto-save
- 카메라: showUrineCamera directly
VoidingDiaryView: sort date groups descending (newest first)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CatheterizeSheet:
- Replace OutlinedButton with filled Button for tab selection
- Selected tab: filled color, unselected: 10% opacity background
- Tabs now reliably switch between 수기/음성/카메라
- Camera tab directly calls onCameraRequested + onDismiss
PiezoMonitoringView:
- Fix camera launch: use pendingCamera + LaunchedEffect
to ensure ModalBottomSheet fully closes before camera opens
- Prevents state race condition between sheet dismiss and camera show
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VoidingDiaryView:
- Date-grouped record list (grouped by day)
- Shows volume/awake/sleep with icons and colors
- Method badge (수기/음성/카메라)
- Delete with confirmation dialog
- Total record count footer
Navigation changes:
- Piezo screen: ArrowBack → Home icon (집 아이콘)
- Piezo screen: 배뇨일지 icon (Description) added next to home
- NIRS screen: ArrowBack → Home icon
- AppScreen.VOIDING_DIARY route added to Crossfade navigation
- VoidingDiaryView navigates back to HOME
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UrineCameraScreen:
- YOLO real-time cup detection with green border indicator
- Auto-capture: 2s continuous detection (≥90%) triggers shutter
- Manual shutter button fallback
- V2 SimpleMeasureService analysis (OCR + 3-zone gradient)
- Auto-analyze after capture
- Result display with volume (ml)
- Save to local VoidingRecordStore + toast confirmation
- Retake option on failure
- Brightness guide text
PiezoMonitoringView:
- Camera tab in CatheterizeSheet now opens UrineCameraScreen
- Full-screen overlay for camera (doesn't leave Piezo screen)
- Auto bladder reset on successful measurement
Complete Catheterize flow:
Catheterize → [수기/음성/카메라] → 입력 → "등록되었습니다" → 레벨 리셋
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Catheterize button now opens a voiding record sheet with 3 input modes:
- Manual: number TextField (1-1000ml)
- Voice: SpeechRecognizer + VolumeParser (Korean/English)
- Camera: placeholder for YOLO cup measurement (TODO)
On save: "배뇨일지에 등록되었습니다" toast, bladder level resets to 0
New modules (from Uridiary):
- measure/: SimpleMeasureService, YoloDetector, CCPosition, VoidingRecord
- speech/: SpeechRecognizerManager, VolumeParser, HfVolumeExtractor
- assets/urinecup_best.onnx: YOLO model for cup detection
VoidingRecordStore: local SharedPreferences+JSON storage for voiding records
Dependencies added: ONNX Runtime, ML Kit OCR, CameraX, OkHttp
Permissions added: CAMERA, RECORD_AUDIO, INTERNET
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Force light theme only, block Samsung Force Dark Mode via forceDarkAllowed=false,
and add explicit text colors to prevent invisible text on light backgrounds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduce padding, icon sizes, and chart dimensions on narrow screens.
Hide title text and battery % in compact mode to prevent top bar
overflow. Scale donut chart 240→180dp and SpO2 text 56→40sp.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add "Demo Mode" button in device scan screen that bypasses BLE
connection and calibration. NIRS monitoring generates simulated
random data at 4Hz. Both monitoring views show DEMO badge in top bar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Y-axis shows data range with 5 tick labels, X-axis shows elapsed time
in seconds, horizontal grid lines for readability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>