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:
+98
-20
@@ -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
|
||||||
|
|||||||
@@ -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 전체 포팅 여부 결정 |
|
||||||
|
|||||||
@@ -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로만 확인 가능) |
|
||||||
|
|||||||
Reference in New Issue
Block a user