docs: v6 전체 업데이트 (1.0.0-design, VBTFW0116 짝, 2026-05-26 기준)

a1042b8(v5) 이후 13개 커밋을 반영해 세 문서 일괄 갱신.

USER_GUIDE.md:
- 헤더: VBTAND0101 → 1.0.0-design (versionCode 24), 권장 펌웨어 VBTFW0116
- PIN DEMO 자동통과 안내 (배포 전 정리 필요)
- Personalization 평행 2-카드 (Maximum Bladder Capacity + Catheter Threshold)
- Sensor Alignment 리디자인 (38sp 큰 타이틀, pubic bone 빨강, 3-stop gradient, Canvas 화살표/아치 제거)
- Measurement Screen top bar에 Home 아이콘 + Voiding/ 텍스트 + 48sp Recorded Dialog
- Auto Scan 600ms로 단축 + 측정 사이클 ~330ms 실측 안내
- 설정 패널 폰/태블릿 자동 폰트 분기 (fontScale 표 추가)
- Dev Mode 추가 기능 (DeviceScan 측정 직진입 버튼, Status 더미 주입)
- Navigation에 placementFromMonitoring 라우팅 분기 설명 + startFromHome fix

VesiScan_Android_Pipeline_Summary.md:
- v6 주요 변경 요약 (헤더에 한눈 보기)
- §5.1 BLE 통신: Connection Parameter 협상 (MTU 247, CONN_PRIORITY HIGH), maa throttle
  state-based gate, 측정 사이클 실측 표
- 측정 흐름: Auto/Single Scan 600ms 동기화 + Voiding 시 자동 stop
- 연속 스캔 동작: Placement loop 1000 → 600ms + v6 화면 디자인 변경 박스
- §14 BLE 명령어 포맷: VBTFW0116 신규 사항 (pending slot 1→8, 안드로이드 짝꿍 변경)
- §22 향후 과제: v6 완료 항목 12개 추가

docs/ALGORITHM_COMPARISON.md:
- Placement loop 1s → 600ms, canSendMaa 게이트 단계 명시
- v6 BLE 사이클 실측 (330ms 평균)
- Measurement Modes: 800 → 600ms, Voiding 자동 stop
- 신규 섹션: BLE maa Throttle — State-Based Gate (배경/구현/효과/logcat 키워드)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 10:26:00 +09:00
parent 31e9b8acbb
commit 4252464ef6
3 changed files with 296 additions and 32 deletions
+98 -20
View File
@@ -1,7 +1,8 @@
# VesiScan-Basic User Guide # VesiScan-Basic User Guide
## App Version: VBTAND0101 ## App Version: 1.0.0-design (versionCode 24)
## Last Updated: 2026-05-11 ## Last Updated: 2026-05-26
## Recommended Firmware: **VBTFW0116** (pending slot 1→8 확장, ADC drop 안정)
--- ---
@@ -11,10 +12,13 @@
1. Install the app and open it 1. Install the app and open it
2. Complete the onboarding screens 2. Complete the onboarding screens
3. Register your information (name, age, height, weight) 3. Register your information (name, age, height, weight)
4. Set a 4-digit PIN 4. ~~Set a 4-digit PIN~~ — **현재 빌드는 PIN 데모 자동통과**: PinView 진입 시 즉시 verified 처리 (시연용)
5. You will arrive at the **Home** screen 5. You will arrive at the **Home** screen
> ⚠️ **DEMO 빌드 표시**: 현재 빌드는 PIN 잠금이 우회됩니다 ([PinView.kt:23-29](app/src/main/java/com/example/medilightv2android/ui/views/pin/PinView.kt#L23-L29)). 원본 UI는 `if (false) { ... }` 안에 보존되어 있어 배포 시 1줄 제거로 복구 가능. 배포 전 정리 체크리스트 참조.
### Home Screen ### Home Screen
- 큰 **Bladdy 캐릭터** (3× 확대, 최대 660dp, 작은 화면에선 90% 캡)
- Tap **Start** to begin - Tap **Start** to begin
- The app version is displayed below "Smart Bladder Monitoring" - The app version is displayed below "Smart Bladder Monitoring"
- To enable Developer Mode: tap the Bladdy character **3 times quickly** (within 1.5 seconds) - To enable Developer Mode: tap the Bladdy character **3 times quickly** (within 1.5 seconds)
@@ -32,9 +36,25 @@
- The app waits up to 30 seconds for pairing to complete - The app waits up to 30 seconds for pairing to complete
5. Once paired, you will move to the Personalization screen 5. Once paired, you will move to the Personalization screen
### Personalization ### Personalization (전면 리디자인 — 평행 2-카드)
- Set your **Max Volume** (200-800 mL, default: 500 mL)
- Tap **Continue** to proceed to Sensor Alignment 상하 정렬된 두 카드로 구성:
**Card 1: Maximum Bladder Capacity**
- 큰 숫자 표시 (예: `500` 64sp ExtraBold + `mL` 24sp)
- 56dp 원형 솔리드 **StepperButton −/+** 좌우 배치
- 슬라이더 (200~800 mL, 50 단위, `roundToInt()` 절삭 fix 적용)
- 카드 헤더 34sp ExtraBold
**Card 2: Catheter Threshold**
- 3분할 텍스트: `Level` 24sp + `7` 64sp + `/10` 24sp (Card 1과 픽셀 매칭)
- 56dp StepperButton −/+
- 슬라이더 Level 1~10 (`roundToInt` 적용)
- 카드 헤더 34sp ExtraBold
두 카드는 64dp 간격으로 수직 중앙 정렬. Bladdy 히어로는 상단에 동적 사이징.
Tap **Continue** to proceed to Sensor Alignment.
--- ---
@@ -42,6 +62,13 @@
This step ensures the sensor is positioned correctly over your bladder for accurate measurement. This step ensures the sensor is positioned correctly over your bladder for accurate measurement.
### 화면 디자인 (리디자인 적용)
- **큰 타이틀 "Sensor Alignment"** 38sp ExtraBold 검정 (Maximum Bladder Capacity 톤 통일)
- **3-stop vertical gradient 배경** (MlTeal 14% / 흰 / MlPrimary 10%) — GREEN 진입 시 단색 톤으로 덮임
- **Hint 텍스트에서 "pubic bone" 인라인 빨강 ExtraBold 강조**
- Canvas 방향 화살표 제거 — hint 텍스트만으로 방향 안내 (이전 4종 화살표 + 치골 아치 라인+halo+endpoint 모두 제거)
- 토르소 실루엣 상단 16% UP shift (chestY 0.06 → 0.09)
### Before Starting ### Before Starting
- Place VesiScan above the pubic bone - Place VesiScan above the pubic bone
- Apply ultrasound gel between the sensor and your skin - Apply ultrasound gel between the sensor and your skin
@@ -102,23 +129,29 @@ After alignment, you will see the main measurement screen with a donut chart.
### Screen Layout ### Screen Layout
``` ```
[Placement] [Battery] [Catheters] [Settings] [Home] [Placement] [Battery] [Catheters] [Settings] ← Home 아이콘 추가됨
Enjoy your day! Enjoy your day!
┌─────────────────┐ ┌─────────────────┐
│ Donut Chart │ │ Donut Chart │ ← 너비 × 0.85 적응형 (이전 0.75 + 360dp cap 제거)
│ │ │ │
│ [Bladdy] │ │ [Bladdy] │ ← 0.483 비율 (이전 0.55에서 -15% 후 +15%)
│ 325 mL │ │ 325 mL │ ← 72sp ExtraBold (이전 24sp의 3배), letterSpacing -1
└─────────────────┘ └─────────────────┘
Current Measurement | 65% Current Measurement | 65%
280 mL | 325/500 mL 280 mL | 325/500 mL
[Void/Catheterization] [Auto Scan] [Single Scan] [Voiding/ [Auto Scan] [Single Scan]
Catheterization]
``` ```
### Top Bar
- **Home 아이콘** (좌측 추가) — `appState.goHome()` 호출, 즉시 홈으로
- **Placement 아이콘** (Home 옆) — `enterPlacementFromMonitoring()` 호출 → Back 시 Monitoring으로 복귀
- 배터리 / 카테터 카운트 / Settings 톱니바퀴는 기존과 동일
### Understanding the Display ### Understanding the Display
| Element | Description | | Element | Description |
@@ -135,12 +168,15 @@ After alignment, you will see the main measurement screen with a donut chart.
## 5. Auto Scan ## 5. Auto Scan
1. Tap the **Auto Scan** button (orange) 1. Tap the **Auto Scan** button (orange)
2. The device will measure continuously (minimum 800ms interval) 2. The device will measure continuously (**minimum 600ms interval**, 이전 1500ms에서 단축)
3. For the first 5 measurements, the display will show **"—"** (collecting data) 3. For the first 5 measurements, the display will show **"—"** (collecting data)
4. After 5+ measurements, the **trimmed mean** value will be displayed (10-sample window, top/bottom excluded) 4. After 5+ measurements, the **trimmed mean** value will be displayed (10-sample window, top/bottom excluded)
5. The donut chart will update every 5 seconds 5. The donut chart will update every 5 seconds
6. Tap **Stop Scan** (red) to end Auto measurement 6. Tap **Stop Scan** (red) to end Auto measurement
> **속도 향상 배경 (2026-05-26)**: FW VBTFW0116 + MTU 247 + CONNECTION_PRIORITY_HIGH 조합으로
> 한 측정 사이클이 ~330ms로 단축. 이전(~1.2초) 대비 **4배 향상**. 자세한 내용은 Pipeline Summary §5.1 참조.
### Auto Measurement Failure ### Auto Measurement Failure
- If 2+ channels fail **5 times in a row** (sensor shifted or lost contact): - If 2+ channels fail **5 times in a row** (sensor shifted or lost contact):
- A dialog will appear: **"Position lost. Restart from alignment"** - A dialog will appear: **"Position lost. Restart from alignment"**
@@ -161,13 +197,14 @@ After alignment, you will see the main measurement screen with a donut chart.
--- ---
## 7. Void / Catheterization ## 7. Voiding / Catheterization (텍스트 변경)
1. Tap the **Void / Catheterization** button 1. Tap the **Voiding / Catheterization** button (이전 "Void/Catheterization" → **"Voiding/"** 로 첫 줄 갱신)
2. The current measurement is recorded to the voiding diary 2. The current measurement is recorded to the voiding diary
3. The catheter count decreases by 1 3. **자동: Auto Scan 진행 중이면 stop** + 측정 상태 정규화 (`displayMaxVolumeMl`/window/timer 모두 0)
4. The bladder level resets to 0 4. The catheter count decreases by 1
5. A toast message **"Recorded"** will appear 5. The bladder level resets to 0
6. **큰 "Recorded" 다이얼로그** (48sp ExtraBold 흰색, MlPrimary 0.95 alpha 배경, 1.5초 자동 닫힘) — 이전 Toast 대체
### Managing Catheters ### Managing Catheters
- The catheter count is shown at the top of the screen (hospital icon + number) - The catheter count is shown at the top of the screen (hospital icon + number)
@@ -183,8 +220,8 @@ Tap the **gear icon** at the top right to open settings.
### General Settings (always visible) ### General Settings (always visible)
| Setting | Description | Default | | Setting | Description | Default |
|---------|-------------|---------| |---------|-------------|---------|
| Max Volume | Maximum bladder volume for fill calculation | 500 mL | | Bladder Capacity | Maximum bladder volume for fill calculation | 500 mL |
| Catheter Threshold | Alert level for catheterization | Level 7 | | Catheter Threshold | Alert level for catheterization | Level 8 |
### Developer Settings (Developer Mode only) ### Developer Settings (Developer Mode only)
| Setting | Description | | Setting | Description |
@@ -196,17 +233,58 @@ Tap the **gear icon** at the top right to open settings.
| SG Filter | Savitzky-Golay noise filter on/off | | SG Filter | Savitzky-Golay noise filter on/off |
| Post Max | Maximum sample index for wall detection (filters floor reflections) | | Post Max | Maximum sample index for wall detection (filters floor reflections) |
### 폰/태블릿 자동 폰트 분기 (2026-05-26)
설정 패널 라벨 크기는 화면 너비에 따라 자동 조절:
```kotlin
val fontScale = (screenWidth / 360f).coerceIn(1.0f, 1.8f)
val isTablet = screenWidth >= 600
labelSize = (14 * fontScale).sp // 폰 14sp, 태블릿 ~25sp
rowSpacing = (6 * fontScale).dp // 폰 6dp, 태블릿 ~10dp
dividerPadding = if (isTablet) 12.dp else 0.dp
```
| 기기 | screenWidth | labelSize |
|---|---|---|
| Galaxy S20 (폰) | 360dp | 14sp |
| Pixel 7 (폰) | 412dp | 16sp |
| 갤탭 S6 Lite | 600dp | 23sp |
| 갤탭 S8 / iPad Pro | 800dp+ | 25sp (cap) |
이전 시도(28sp 하드코딩)에서 폰 화면이 곱창나던 문제를 해결.
### Dev Mode 추가 기능
- **HomeView Bladdy 3탭** → Developer Mode 토글
- **DeviceScanView "No devices found" 화면 + dev 모드** → "Enter Measurement Mode (Dev)" 버튼 (BLE 없이 측정 화면 진입)
- **PiezoMonitoring Status 배지 dev 탭** → 60~maxVol-50 범위 더미 측정값 랜덤 주입 (BLE 없이 동작 검증용)
- **dev 모드 한정 ChannelPanel / BleDebugPanel** 표시 (Developer Tools 버튼 토글)
--- ---
## 9. Navigation ## 9. Navigation
| Action | Result | | Action | Result |
|--------|--------| |--------|--------|
| **Placement button** (top left) | Go to Sensor Alignment | | **Home button** (top left) | Go to Home screen (`appState.goHome()`) |
| **Placement button** (Home 옆) | Re-enter Sensor Alignment (`enterPlacementFromMonitoring()`) |
| **Settings button** (top right) | Open/close settings panel | | **Settings button** (top right) | Open/close settings panel |
| **Back button** (Android) | Go to previous screen | | **Back button** (Android) | Go to previous screen |
| **Back button on Home** | "Exit App?" confirmation dialog | | **Back button on Home** | "Exit App?" confirmation dialog |
### Placement 진입 경로 분기 (2026-05-12 신규)
`placementFromMonitoring` 플래그로 Back 동작이 자동 분기:
- **Personalization → Placement → Monitoring** (정방향): Placement에서 Back = Personalization으로 복귀
- **Monitoring → Placement** (재정렬): Placement에서 Back = Monitoring으로 복귀
코드: [AppState.kt](app/src/main/java/com/example/medilightv2android/AppState.kt) 의 `enterPlacementFromMonitoring()` / `backFromPlacementGuide()`.
### startFromHome() 라우팅 fix
페어링 완료 상태에서 Home → Start 시 Personalization부터 시작 (이전: Monitoring 직행 버그).
--- ---
## 10. Troubleshooting ## 10. Troubleshooting
+126 -7
View File
@@ -2,7 +2,20 @@
## 작성일: 2026-04-23 ## 작성일: 2026-04-23
## 작성자: dwjang ## 작성자: dwjang
## 마지막 업데이트: 2026-05-11 (v5) — VBTAND0101 ## 마지막 업데이트: 2026-05-26 (v6) — 1.0.0-design (versionCode 24)
## 권장 펌웨어: **VBTFW0116** (pending slot 1→8 확장으로 ADC drop 안정)
### v6 주요 변경 요약 (2026-05-12 ~ 2026-05-26)
- **BLE throttle 상태 기반 게이트화** — 시간 단독(800ms)에서 `isMultiChannel && !isComplete` + 시간(600ms) 복합 게이트로 전환. 잔여 reb 폐기로 인한 CH0/CH1만 도착 + BV_FAIL/BV_SKIP 증상 해소
- **CONNECTION_PRIORITY_HIGH 요청 추가** — CCCD write 완료 시점에 `gatt.requestConnectionPriority(HIGH)` 호출. peripheral이 수락 시 interval ~15ms로 단축
- **MTU 247 협상 확정** — VBT26050202 + VBTFW0116 조합에서 247 정상 협상 (logcat 검증)
- **측정 사이클 시간 1.2s → 0.33s** (4배 향상). Placement loop 1000ms → 600ms, Auto loop 1500ms → 600ms
- **PinView DEMO 우회**, **DeviceScanView Dev 측정모드 진입 버튼**, **AppState placementFromMonitoring 라우팅 분기** 추가
- **PiezoPersonalizationView 평행 2-카드 리디자인** (Maximum Bladder Capacity + Catheter Threshold, StepperButton, roundToInt fix)
- **PlacementGuideView Sensor Alignment 리디자인** (3-stop gradient, 38sp 큰 타이틀, "pubic bone" 빨강 강조, Canvas 화살표/아치 제거, chestY 0.09)
- **PiezoMonitoringView 누적 업데이트** (Voiding/ 텍스트, 48sp Recorded Dialog, useScrollLayout, Volume 72sp ExtraBold, Bladdy 0.483, Home 아이콘, 설정 패널 폰/태블릿 자동 분기)
- **HomeView Bladdy 3× 확대** (220 → 660dp + BoxWithConstraints 90% 캡)
--- ---
@@ -122,6 +135,68 @@ reb:(4바이트) + repeat_count(2바이트) + ADC데이터(200바이트) + CRC(2
└ "reb:" 문자 └ 항상 100 └ 100개 × 2바이트(16-bit) └ 체크섬 └ "reb:" 문자 └ 항상 100 └ 100개 × 2바이트(16-bit) └ 체크섬
``` ```
### Connection Parameter 협상 (v6 신규)
**MTU 247**: 연결 직후 `gatt.requestMtu(247)` 호출. 208B `reb:` 패킷을 단일 노티로 전송하여
단편화 drop 위험 제거. 실측 VBT26050202 + VBTFW0116에서 247 정상 협상 확인.
**CONNECTION_PRIORITY_HIGH**: CCCD write 성공 직후(서비스 ready 시점) 호출:
```kotlin
gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)
debugLogger.info("CONN_PRIORITY HIGH requested (ok=$priorityOk)")
```
peripheral이 connection parameter update를 수락하면 interval ~15ms로 협상되어
Central Link Layer ACK 속도가 향상됨 → 펌웨어 SoftDevice TX queue 포화 빈도 감소.
수락 여부는 안드로이드 API로 직접 확인 불가(HCI 레벨 차단) — 응답 시간 측정으로 간접 확인.
### maa Throttle (v6 상태 기반 게이트로 강화)
이전: 시간 단독 `now - lastMaaSentMs < 800` 차단 → 펌웨어 응답이 1.2s 걸릴 때
`startMultiChannel(6)`이 응답 도중 호출되어 잔여 reb 패킷이 폐기되는 race 발생.
신규: [BleManager.kt:464-481](app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L464-L481) `canSendMaa()`
```kotlin
private fun canSendMaa(caller: String): Boolean {
val now = System.currentTimeMillis()
val collectorBusy = piezoCollector.isMultiChannel && !piezoCollector.isComplete
val sinceLast = now - lastMaaSentMs
if (collectorBusy) {
if (sinceLast < 3000) {
Log.w("BleManager", "maa BUSY [$caller] — prev response in progress")
return false // (A) 상태 차단
}
Log.w("BleManager", "maa FORCE [$caller] — prev incomplete after ${sinceLast}ms")
}
if (sinceLast < 600) {
Log.w("BleManager", "maa THROTTLED [$caller] — ${sinceLast}ms since last")
return false // (B) 시간 차단
}
return true
}
```
| 게이트 | 차단 조건 | 풀림 조건 |
|---|---|---|
| (A) State | `isMultiChannel && !isComplete` (raa 미도착) | raa 도착 OR 3초 경과 (FORCE) |
| (B) Time | 마지막 송신 후 600ms 미만 | 600ms 경과 |
두 게이트 모두 통과해야 maa 송신. State gate가 안전망, time gate가 펌웨어 burst 방어.
### 측정 사이클 실측 (VBTFW0116 + MTU 247 + CONN_PRIORITY HIGH)
logcat 측정 (2026-05-26):
| 사이클 | TX maa | RX raa | 소요 |
|---|---|---|---|
| Placement #1 | 57.333 | 57.652 | **319ms** |
| Monitor #1 | 00.952 | 01.233 | **281ms** |
| Monitor #2 | 02.851 | 03.191 | **340ms** |
| Monitor #3 | 04.414 | 04.719 | **305ms** |
| Monitor #4 | 05.982 | 06.292 | **310ms** |
| Monitor #5 | 07.549 | 07.972 | **423ms** |
**평균 ~330ms**. 이전 ~1.2~1.4s 대비 4배 향상. 모든 사이클에서 6/6 채널 도착, 유실 0건.
### Big Endian 자동 감지 ### Big Endian 자동 감지
``` ```
@@ -480,7 +555,7 @@ Start Scanning 버튼 활성화 (초록색)
| 위치 이탈 | "Position lost. Restart above pubic bone." | | 위치 이탈 | "Position lost. Restart above pubic bone." |
| 센서 미부착 | "Sensor detached. Restart above pubic bone." | | 센서 미부착 | "Sensor detached. Restart above pubic bone." |
## 연속 스캔 동작 ## 연속 스캔 동작 (2026-05-26 업데이트)
``` ```
"Start Alignment" 버튼 탭 (waitingForStart → false) "Start Alignment" 버튼 탭 (waitingForStart → false)
@@ -489,7 +564,7 @@ Start Scanning 버튼 활성화 (초록색)
↓ ↓
즉시 첫 스캔 (maa 6채널) 즉시 첫 스캔 (maa 6채널)
↓ 결과: CV, 방향, Score 표시 (28sp 대형 힌트) ↓ 결과: CV, 방향, Score 표시 (28sp 대형 힌트)
1초 대기 600ms 대기 (이전 1000ms → 600ms 단축, BleManager throttle과 동기화)
↓ ↓
자동 재스캔 (scanCount 증가 → CV threshold 완화) 자동 재스캔 (scanCount 증가 → CV threshold 완화)
↓ ↓
@@ -498,6 +573,15 @@ Start Scanning 버튼 활성화 (초록색)
is_pass = true → Lock → 7초 hold → "Start Scanning" 버튼 활성화 is_pass = true → Lock → 7초 hold → "Start Scanning" 버튼 활성화
``` ```
### v6 화면 디자인 변경
- 큰 "Sensor Alignment" 38sp ExtraBold 검정 (Maximum Bladder Capacity 카드와 톤 통일)
- Hint 텍스트 "pubic bone" 인라인 빨강 ExtraBold (AnnotatedString + SpanStyle)
- 3-stop vertical gradient 배경 (MlTeal 14% / 흰 / MlPrimary 10%) — GREEN 진입 시 단색 톤
- Canvas 방향 화살표 4종 제거 (hint 텍스트만으로 안내)
- 치골 아치 라인 + halo + endpoint 모두 제거
- 토르소 chestY 0.06 → 0.09 (실루엣 위로 16% shift)
- Body 상단 Spacer 8dp
--- ---
# 7. "Spot" 버튼 측정 흐름 # 7. "Spot" 버튼 측정 흐름
@@ -534,20 +618,22 @@ Spot 버튼 탭
화면에 "305 ml" 표시! (도넛차트 + 레벨 업데이트) 화면에 "305 ml" 표시! (도넛차트 + 레벨 업데이트)
``` ```
## Auto Scan 모드 (2026-05-11 업데이트) ## Auto Scan 모드 (2026-05-26 업데이트)
"Auto Scan" 버튼으로 자동 반복 측정: "Auto Scan" 버튼으로 자동 반복 측정:
- 최소 800ms 간격 자동 측정 (BLE maa throttle 보장) - **최소 600ms 간격** 자동 측정 (BLE maa state-based throttle 보장)
- LaunchedEffect loop delay: 1500ms → **600ms** (FW VBTFW0116 + MTU 247 + CONN_PRIORITY HIGH 적용 후 응답 ~330ms이므로 일관 단축)
- 첫 5회: "—" 표시 (데이터 수집 중) - 첫 5회: "—" 표시 (데이터 수집 중)
- 5회 이상: **sliding window trimmed mean** (10개 윈도우, 최대/최소 각 1개 제외 → 8개 평균) - 5회 이상: **sliding window trimmed mean** (10개 윈도우, 최대/최소 각 1개 제외 → 8개 평균)
- "Stop Scan" 버튼으로 종료 - "Stop Scan" 버튼으로 종료
- **Voiding/Catheterization 버튼 클릭 시 Auto 자동 stop + 측정 상태 정규화** (displayMaxVolumeMl/window/timer 모두 0) — v6 신규
- Auto fail: 2ch+ missing 5회 연속 → "Position lost. Restart from alignment" 다이얼로그 - Auto fail: 2ch+ missing 5회 연속 → "Position lost. Restart from alignment" 다이얼로그
## Single Scan 모드 (2026-05-11 업데이트) ## Single Scan 모드 (2026-05-11 업데이트)
"Single Scan" 버튼 1탭 → **5회 측정 → trimmed mean**: "Single Scan" 버튼 1탭 → **5회 측정 → trimmed mean**:
- 최대 8회까지 재시도 (유효값 5개 수집 목표, 최소 3개) - 최대 8회까지 재시도 (유효값 5개 수집 목표, 최소 3개)
- 측정 간격: maa 800ms throttle이 간격 보장 - 측정 간격: maa 600ms throttle이 간격 보장 (v6에서 800 → 600 단축)
- 중간 값 화면 미표시 (isSpotInProgress 플래그) - 중간 값 화면 미표시 (isSpotInProgress 플래그)
- 1초 쿨다운 후 재사용 가능 - 1초 쿨다운 후 재사용 가능
@@ -626,6 +712,8 @@ medilightv2android/
│ └── (onboarding, registration, pin, ...) │ └── (onboarding, registration, pin, ...)
│ │
└── AppState.kt ← 전역 상태 관리 + 화면 라우팅 └── AppState.kt ← 전역 상태 관리 + 화면 라우팅
(v6: placementFromMonitoring 플래그 +
enterPlacementFromMonitoring() / backFromPlacementGuide())
``` ```
## 포팅 원본 대조 ## 포팅 원본 대조
@@ -875,7 +963,26 @@ RX: "raa all-ch complete [8B]"
# 14. BLE 명령어 포맷 (펌웨어 특이사항) # 14. BLE 명령어 포맷 (펌웨어 특이사항)
이 기기(VBTFW0111)는 파라미터 없는 명령어에도 **`cmd? `(물음표+공백)** 포맷이 필요합니다. 이 기기(VBTFW0111 ~ **VBTFW0116**)는 파라미터 없는 명령어에도 **`cmd? `(물음표+공백)** 포맷이 필요합니다.
### VBTFW0116 신규 사항 (펌웨어 측 패치)
```
원인:
- Central(앱)의 Link Layer ACK / connection event 처리 속도보다 Peripheral이
notify enqueue를 더 빨리 시도해서 SoftDevice TX queue 포화
- 기존 FW: pending slot 1개만 보관 (이미 pending 있으면 다음 패킷 drop)
- PC 환경에선 Central 처리 빨라 문제 없음 → 실제 앱 환경에선 OS/connection 영향으로 포화 잦음
개선:
- pending slot 1 → 8개 확장 → TX queue 포화 시 보관/재전송 → 중간 채널 데이터 유실 감소
- 결과: ADC drop 현상 재현되지 않음 (장시간 반복 테스트 진행 중)
안드로이드 측 짝꿍 변경:
- MTU 247 협상 (208B reb 단일 노티)
- CONNECTION_PRIORITY_HIGH 요청 (interval 단축 시도)
- maa throttle을 state-based gate로 강화 (잔여 reb 폐기 방지)
```
```kotlin ```kotlin
// ❌ 안 됨 (rxs: cmd_not_supported 응답) // ❌ 안 됨 (rxs: cmd_not_supported 응답)
@@ -1313,6 +1420,18 @@ ADC 파싱 시 Big/Little Endian을 자동 감지:
| 앱 아이콘 | ✅ 완료 | 방광이 캐릭터 PNG (5 density) | | 앱 아이콘 | ✅ 완료 | 방광이 캐릭터 PNG (5 density) |
| Placement 화살표 수정 | ✅ 완료 | 디바운스/보호 중에도 화살표 항상 표시 | | Placement 화살표 수정 | ✅ 완료 | 디바운스/보호 중에도 화살표 항상 표시 |
| GREEN 안정성 | ✅ 완료 | 7초 hold + 3회 연속 실패 exit + phase 보호 | | GREEN 안정성 | ✅ 완료 | 7초 hold + 3회 연속 실패 exit + phase 보호 |
| MTU 247 + CONN_PRIORITY HIGH | ✅ 완료 (v6) | VBT26050202 + VBTFW0116 정상 협상 |
| maa throttle state-based gate | ✅ 완료 (v6) | isComplete + 600ms + 3s FORCE |
| 측정 사이클 1.2s → 0.33s | ✅ 완료 (v6) | 4배 향상, 6/6 유실 0 |
| Placement loop 600ms 단축 | ✅ 완료 (v6) | 1000 → 600ms |
| Auto loop 600ms 단축 | ✅ 완료 (v6) | 1500 → 600ms |
| placementFromMonitoring 라우팅 분기 | ✅ 완료 (v6) | enterPlacementFromMonitoring/backFromPlacementGuide |
| PiezoPersonalization 평행 2-카드 | ✅ 완료 (v6) | Maximum Bladder Capacity + Catheter Threshold |
| PlacementGuide Sensor Alignment 리디자인 | ✅ 완료 (v6) | 3-stop gradient + 38sp 큰 타이틀 + pubic bone 빨강 |
| PiezoMonitoring Home 아이콘 / Voiding/ / Recorded Dialog | ✅ 완료 (v6) | |
| HomeView Bladdy 3× (660dp 캡) | ✅ 완료 (v6) | BoxWithConstraints 90% cap |
| 설정 패널 폰/태블릿 자동 분기 | ✅ 완료 (v6) | fontScale (360dp→1.0, 600dp+→1.8) |
| PinView DEMO 자동통과 | ✅ DEMO 빌드 한정 | 배포 전 if(false) 블록 제거 |
| IMU 모션 감지 | 🔜 예정 | vesiscanbasicAndroid에서 포팅 예정 | | IMU 모션 감지 | 🔜 예정 | vesiscanbasicAndroid에서 포팅 예정 |
| Placement 팬텀 테스트 | 🔜 예정 | Gradient vs SWEEP 비교 | | Placement 팬텀 테스트 | 🔜 예정 | Gradient vs SWEEP 비교 |
| 5-Anchor calibration | 🔜 검토 | CKLaw 전체 포팅 여부 결정 | | 5-Anchor calibration | 🔜 검토 | CKLaw 전체 포팅 여부 결정 |
+72 -5
View File
@@ -133,17 +133,20 @@ EDGE_DIST_DECAY=0.12, VALLEY_STOP_RISE=50, etc.
## Placement Test Algorithm ## Placement Test Algorithm
### Overview (2026-05-11 updated) ### Overview (2026-05-26 updated — v6)
``` ```
Screen entry → "Place VesiScan above the pubic bone, then press Start" Screen entry → "Place VesiScan above the pubic bone, then press Start"
↓ Start Alignment 버튼 탭 (waitingForStart=false) ↓ Start Alignment 버튼 탭 (waitingForStart=false)
전체 상태 리셋 전체 상태 리셋
↓ ↓
[1-second interval loop] [600ms interval loop] ← v6: 1000ms → 600ms (BleManager throttle과 동기화)
↓ ↓
mpa (first time, 500ms delay) → maa → 6ch reb mpa (first time, 500ms delay) → maa → 6ch reb
↓ ↓ (maa 전 BleManager.canSendMaa() 게이트 통과 필요)
│ - State gate: !piezoCollector.isComplete 이면 차단 (3s 후 FORCE)
│ - Time gate: 마지막 송신 < 600ms 이면 차단
│
Detachment check (6ch received, THR=30) → LED 4 if detached Detachment check (6ch received, THR=30) → LED 4 if detached
↓ ↓
Wall detection (Method A/B/C) → urineLen ≥ 12 Wall detection (Method A/B/C) → urineLen ≥ 12
@@ -161,6 +164,11 @@ GREEN: 7s hold + 3 consecutive fail to exit
Screen exit → LED OFF Screen exit → LED OFF
``` ```
### v6 BLE 사이클 실측 (VBTFW0116 + MTU 247 + CONN_PRIORITY HIGH)
한 측정 사이클 (TX maa → RX raa): **평균 ~330ms** (이전 ~1.2~1.4s 대비 4배 향상).
600ms loop delay 안에 한 사이클이 안정적으로 완료됨.
### Guide Modes ### Guide Modes
**Gradient** (default): weighted center gradient → LATERAL → GREEN **Gradient** (default): weighted center gradient → LATERAL → GREEN
@@ -173,13 +181,14 @@ Screen exit → LED OFF
**Stage 2 LATERAL**: |len4-len5| ≤ 10 → GREEN, else "Slide left/right ←→" **Stage 2 LATERAL**: |len4-len5| ≤ 10 → GREEN, else "Slide left/right ←→"
**Stage 3 GREEN**: CV ≤ threshold AND |LR dev| ≤ 0.20 → "In position!" (7s hold) **Stage 3 GREEN**: CV ≤ threshold AND |LR dev| ≤ 0.20 → "In position!" (7s hold)
### Measurement Modes ### Measurement Modes (v6 업데이트)
- **Auto Scan**: min 800ms gap, 10-sample trimmed mean (max/min removed, 8 avg) - **Auto Scan**: **min 600ms gap** (state-based throttle: `!isComplete` + 600ms), 10-sample trimmed mean (max/min removed, 8 avg)
- **Single Scan (Spot)**: 5회 측정 → trimmed mean (max 8 attempts, min 3) - **Single Scan (Spot)**: 5회 측정 → trimmed mean (max 8 attempts, min 3)
- valid ≥ 4 channels only - valid ≥ 4 channels only
- Analysis on coroutine (Dispatchers.Default) - Analysis on coroutine (Dispatchers.Default)
- Auto fail: 2ch+ missing 5 consecutive → realign dialog - Auto fail: 2ch+ missing 5 consecutive → realign dialog
- **Voiding/Catheterization 클릭 시 Auto Scan 자동 stop + 측정 상태 정규화** (displayMaxVolumeMl/window/timer 모두 0)
- Detachment → LED 4 (once), recovery → LED 0 (once) - Detachment → LED 4 (once), recovery → LED 0 (once)
- Foreground Service: BLE connection maintained during screen off - Foreground Service: BLE connection maintained during screen off
- BLE 로그: 실시간 파일 기록 (Downloads/VesiScan_BLE_*.log, 화면 잠금에도 보존) - BLE 로그: 실시간 파일 기록 (Downloads/VesiScan_BLE_*.log, 화면 잠금에도 보존)
@@ -203,3 +212,61 @@ GREEN exit: 3회 연속 실패 (greenFailCount)
±15% 이내: 223/223 (100%) ±15% 이내: 223/223 (100%)
범위: 441~537ml 범위: 441~537ml
``` ```
---
## BLE maa Throttle — State-Based Gate (v6, 2026-05-26)
### 배경: CH0/CH1만 도착 + BV_FAIL/BV_SKIP 증상
이전 throttle은 시간 단독 `now - lastMaaSentMs < 800ms` 만 검사. 펌웨어 응답이
~1.2~1.4초 걸리는 환경에서 800ms 시점에 새 maa가 송신되면:
1. `sendChannelsOnly()` 통과 → `piezoCollector.startMultiChannel(6)` → collector reset
2. **큐에 떠다니던 이전 사이클의 reb / raa 패킷이 새 collector의 시작 부분에 잘못 들어감**
3. 결과: 한 채널만 들고 raa로 종료 → `BV_FAIL no valid channels` 또는 `BV_SKIP center=0 total=5/6`
실측 로그 (FW VBTFW0111, 2026-05-22):
```
TX maa #1 → reb×5 → (raa 큐 밀림) → TX maa #2 → reb×1 + raa → BV_FAIL scan=656
```
### v6 신규 게이트
```kotlin
private fun canSendMaa(caller: String): Boolean {
val now = System.currentTimeMillis()
val collectorBusy = piezoCollector.isMultiChannel && !piezoCollector.isComplete
val sinceLast = now - lastMaaSentMs
if (collectorBusy) {
if (sinceLast < 3000) return false // (A) state 차단
// 3s 넘으면 강제 통과 (FORCE) — deadlock 방지
}
if (sinceLast < 600) return false // (B) time 차단
return true
}
```
| 게이트 | 차단 | 풀림 |
|---|---|---|
| (A) State | `isMultiChannel && !isComplete` | raa 도착 OR 3s FORCE |
| (B) Time | `sinceLast < 600` | 600ms 경과 |
두 게이트 모두 통과해야 maa 송신. State gate가 race 방어, time gate가 burst 방어.
### 효과 (FW VBTFW0116 + MTU 247 + CONN_PRIORITY HIGH 조합)
- 한 사이클 응답: 1.2s → **330ms** (4배)
- 6/6 채널 도착률: 부분응답 잦음 → **100% 도착, 유실 0건**
- BV_FAIL/BV_SKIP 빈도: 자주 → 관찰되지 않음
### Logcat 키워드
| 로그 | 의미 |
|---|---|
| `maa BUSY [...] — prev response in progress (Xms)` | State gate 차단 (정상 방어) |
| `maa FORCE [...] — prev incomplete after Xms` | 3s 넘게 raa 없음 → 포기하고 진행 |
| `maa THROTTLED [...] — Xms since last` | Time gate 차단 (정상 방어) |
| (로그 없음) | 정상 송신 |
| `MTU changed: 247 (status=0)` | MTU 정상 협상 (CCCD write 직전) |
| `CONN_PRIORITY HIGH requested (ok=true)` | priority 요청 큐잉 성공 (수락 여부는 sniffer로만 확인 가능) |