Files
VesiScan-Basic_Zephyr/plan/system_pipeline.md
T

426 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VesiScan-Basic — 시스템 전체 파이프라인
## 목차
1. [전체 동작 개요](#1-전체-동작-개요)
2. [부팅 및 BLE 연결 흐름](#2-부팅-및-ble-연결-흐름)
3. [커맨드/응답 테이블](#3-커맨드응답-테이블)
4. [msp? 흐름 (1초 주기 IMU)](#4-msp-흐름-1초-주기-imu)
5. [mbb? 흐름 (10초 주기 전체 측정)](#5-mbb-흐름-10초-주기-전체-측정)
6. [구현 현황](#6-구현-현황)
7. [핀 배치 전체 정리](#7-핀-배치-전체-정리)
8. [IMU 테스트 가이드](#8-imu-테스트-가이드-msp)
9. [다음 구현 순서](#9-다음-구현-순서)
---
## 1. 전체 동작 개요
```
[앱] ──BLE NUS── [디바이스]
│ │
│── msp? (1초마다) ──▶ │ IMU 측정 → rsp: (accel+gyro)
│ │
│── mbb? (10초마다) ──▶ │ 6ch 피에조 측정 + 배터리/IMU/온도
│ │ → rbb: (센서 번들)
│◀── rbb: ────────────│
│◀── reb: × 6 ────────│ (채널별 echo 데이터)
│◀── raa: ────────────│ (완료)
```
---
## 2. 부팅 및 BLE 연결 흐름
```
[부팅]
전원 버튼 2초 유지
P0.08 래치 ON (전원 자가유지)
HW 초기화
├─ GPIO, LED
├─ Battery ADC (SAADC AIN2)
├─ IMU (I2C0, ICM42670P @ 0x68)
├─ Temperature ADC (SAADC AIN3, TMP235-Q1)
├─ [TODO] Piezo 드라이버 (GPIO + SW burst)
└─ [TODO] Echo ADC (SPIM3, ADC121S051)
BLE 스택 초기화 (NUS)
Advertising 시작 → LED: 파란불 깜빡임
앱 연결됨 → LED: 연결 표시
앱에서 주기적 커맨드 수신
├─ msp? (1초마다)
└─ mbb? (10초마다)
```
---
## 3. 커맨드/응답 테이블
| 커맨드 | 설명 | 응답 | 주기 | 구현 상태 |
|---|---|---|---|---|
| `msp?` | IMU 1회 측정 | `rsp:` (accel+gyro, 12B) | 1초 | ✅ 완료 |
| `mbb?` | 전체 측정 (센서+피에조 6ch) | `rbb:` + `reb:` ×6 + `raa:` | 10초 | ❌ 미구현 |
| `maa?` | 피에조 6ch 단독 측정 | `reb:` ×6 + `raa:` | 수동 | ❌ 미구현 |
| `msn?` | 배터리 전압 단독 측정 | `rsn:` (mV) | 수동 | ✅ 완료 |
| `mls?` | LED 상태 설정 | `rls:` (state echo) | 수동 | ✅ 완료 |
---
## 4. msp? 흐름 (1초 주기 IMU)
```
앱 → msp?
imu_read(accel, gyro)
├─ GYRO_CONFIG0 = 0x09 (±2000dps, 100Hz)
├─ ACCEL_CONFIG0 = 0x29 (±4g, 100Hz)
├─ PWR_MGMT0 = 0x0F (low-noise ON)
├─ 80ms 대기 (자이로 스타트업)
├─ 0x0B부터 12바이트 읽기
└─ PWR_MGMT0 = 0x00 (슬립)
rsp: [accel X(2)] [accel Y(2)] [accel Z(2)]
[gyro X(2)] [gyro Y(2)] [gyro Z(2)]
[CRC16(2)] = 18 bytes
```
**구현 상태: ✅ 완료**
- `src/drivers/imu/imu_i2c.c` — ICM42670P I2C 드라이버
- `src/parser.c``msp?` 핸들러, `rsp:` 응답
---
## 5. mbb? 흐름 (10초 주기 전체 측정)
```
앱 → mbb?
[Phase 1] 피에조 6채널 캡처 (타이밍 크리티컬)
┌──────────────────────────────────────────────┐
│ piezo_power_on() ← PWR_EN=HIGH, 3ms 대기 │
│ echo_adc_init() ← SPIM3 초기화 │
│ │
│ for ch = 0 ~ 5: │
│ piezo_select_channel(ch) ← MUX, 1.3ms │
│ for avg = 0 ~ 2: ← 3회 평균 │
│ piezo_burst_sw(5) ← 2.1MHz SW burst│
│ delay 20us │
│ echo_adc_capture(buf, 100) ← SPIM3 │
│ channel_data[ch] = 평균값 │
└──────────────────────────────────────────────┘
[Phase 2] 센서 측정 (피에조 캡처 완료 후)
┌──────────────────────────────────────────────┐
│ battery_read_mv() → info_batt (mV) │
│ imu_read(accel, gyro) → info_imu[6] │
│ temperature_read() → info_temp (°Cx100) │
└──────────────────────────────────────────────┘
[Phase 3] BLE 전송
┌──────────────────────────────────────────────┐
│ rbb: [batt(2)][IMU(12)][temp(2)][CRC(2)] │
│ = 20 bytes │
│ │
│ for ch = 0 ~ 5: │
│ reb: [num_samples(2)][raw(200)][CRC(2)] │
│ = 204 bytes per channel │
│ │
│ raa: [status(2)][CRC(2)] = 8 bytes │
└──────────────────────────────────────────────┘
piezo_power_off()
```
**rbb: 패킷 포맷**
```
[r][b][b][:] [batt_L][batt_H]
[imu0_L][imu0_H] ... [imu5_L][imu5_H] ← accel XYZ + gyro XYZ
[temp_L][temp_H]
[CRC_L][CRC_H]
= 4 + 2 + 12 + 2 + 2 = 22 bytes
```
**reb: 패킷 포맷 (채널당)**
```
[r][e][b][:] [num_samples_H][num_samples_L]
[s0_H][s0_L] [s1_H][s1_L] ... [s99_H][s99_L]
[CRC_L][CRC_H]
= 4 + 2 + 200 + 2 = 208 bytes
```
---
## 6. 구현 현황
### ✅ 완료
| 모듈 | 파일 | 기능 |
|---|---|---|
| BLE NUS | `src/ble/ble_service.c` | advertising, 연결, RX/TX |
| 전원 제어 | `src/power_control.c`, `src/main.c` | 버튼 상태머신, 래치, 슬립 |
| LED | `src/drivers/led/led_control.c` | 상태별 LED 패턴 |
| 배터리 ADC | `src/drivers/battery/battery_adc.c` | SAADC AIN2, 주기 모니터링 |
| IMU | `src/drivers/imu/imu_i2c.c` | ICM42670P I2C (SCL=P1.14, SDA=P1.15) |
| 온도 센서 | `src/drivers/temperature/tmp235.c` | TMP235-Q1, SAADC AIN3 (P0.05) |
| 파서 | `src/parser.c` | `msn?`, `mls?`, `msp?` |
#### SAADC 채널 공유 (배터리 ↔ 온도)
배터리(AIN2)와 온도(AIN3)는 nRF52840 SAADC 하나를 공유한다.
**nRF5 SDK에서는** 채널 전환 시 `nrfx_saadc_uninit()`을 명시적으로 호출하지 않으면 다음 `nrfx_saadc_channel_init()` 호출에서 `NRFX_ERROR_BUSY`가 발생하는 문제가 있었다.
**Zephyr에서는** `adc_channel_setup_dt()` + `adc_read_dt()` 내부적으로 init/uninit 처리가 되므로 명시적 uninit 불필요. `battery_read_mv()``temp_read_cdeg()` 모두 읽기 전에 `adc_channel_setup_dt()`를 호출하여 해당 채널로 SAADC를 재설정하기 때문에 임의 순서로 교대 호출해도 정상 동작한다.
> 단, 두 함수를 **서로 다른 스레드에서 동시에** 호출하면 안 됨. mbb? 핸들러에서 직렬로 호출하는 한 문제없음.
### 🔲 테스트 필요
| 모듈 | 테스트 방법 | 확인 항목 |
|---|---|---|
| 배터리 ADC (`msn?`) | RTT 로그 확인 + 앱에서 `msn?` 전송 | 아래 테스트 가이드 참고 |
| 온도 센서 | RTT 로그 확인 | 아래 테스트 가이드 참고 |
| IMU (`msp?`) | RTT 로그 확인 + 앱에서 `msp?` 전송 | 아래 테스트 가이드 참고 |
#### 배터리 ADC 테스트 가이드 (msn?)
**1. 부팅 시 확인 (RTT 로그)**
`[1] HW Init` 단계에서 아래 줄이 나와야 함:
```
[BATT] ADC init OK (DT-based, ch=2, res=12, os=2)
```
실패 패턴:
| 로그 | 원인 | 조치 |
|---|---|---|
| `[BATT] ADC device not ready` | SAADC 드라이버 초기화 실패 | `CONFIG_ADC=y` 확인, overlay channel@2 확인 |
| `[BATT] Channel setup failed (err -x)` | 채널 설정 실패 | overlay AIN2 설정 확인 |
**2. msn? 전송 시 확인 (RTT 로그)**
앱에서 `msn?` 전송 시 아래 형식으로 출력됨:
```
[CMD] msn -> 3800 mV
```
- 정상 범위: 3500 ~ 4200 mV (완충 4200mV, 저전압 경고 3500mV)
- 3500mV 미만이 10회 연속이면 자동 전원 OFF
**3. BLE 응답 패킷 포맷**
```
[r][s][n][:] [mV_H][mV_L] [CRC_L][CRC_H] = 8 bytes
```
---
#### 온도 센서 테스트 가이드 (TMP235)
**1. 부팅 시 확인 (RTT 로그)**
`[1] HW Init` 단계에서 아래 줄이 나와야 함:
```
[TEMP] OK — TMP235 ch=3, res=12, os=2
```
실패 패턴:
| 로그 | 원인 | 조치 |
|---|---|---|
| `[TEMP] FAIL — ADC device not ready` | SAADC 드라이버 초기화 실패 | overlay channel@3 확인 |
| `[TEMP] FAIL — channel setup (err=-x)` | 채널 설정 실패 | overlay AIN3 설정 확인, P0.05 핀 확인 |
**2. 측정 시 확인 (RTT 로그)**
`temp_read_cdeg()` 호출 시 아래 형식으로 출력됨:
```
[TEMP] raw=1234 -> 25.50 C
```
- 실온(25°C) 기준 raw 값 약 1404 (V = 750mV, raw = 750 × 4095 / 3600 ≈ 853)
> 실제 분압 회로가 있으면 값이 다를 수 있음, 첫 측정값 보고 판단
- `INT16_MIN(-32768)` 반환 시 ADC 읽기 실패
**3. mbb? 패킷 내 위치**
```
rbb: ... [temp_L][temp_H] ... ← °C × 100 단위 (25.50°C = 2550)
```
---
#### IMU 테스트 가이드 (msp?)
**1. 부팅 시 확인 (RTT 로그)**
`[1] HW Init` 단계에서 아래 줄이 나와야 함:
```
[IMU] OK — ICM42670P detected (WHOAMI=0x67, addr=0x68)
```
실패 패턴:
| 로그 | 원인 | 조치 |
|---|---|---|
| `[IMU] FAIL — I2C bus not ready` | Zephyr I2C 드라이버 초기화 실패 | `CONFIG_I2C=y` 확인, overlay 확인 |
| `[IMU] FAIL — WHOAMI read error (check SCL=P1.14, SDA=P1.15)` | I2C 통신 자체 실패 | 핀 납땜/연결 확인, 풀업 저항 확인 |
| `[IMU] FAIL — WHOAMI mismatch (got=0x00, expected=0x67)` | 버스는 살아있으나 응답 이상 | I2C 주소(0x68) 확인, AD0 핀 상태 확인 |
**2. msp? 전송 시 확인 (RTT 로그)**
앱에서 `msp?` 전송 시 아래 형식으로 출력됨:
```
[IMU] msp: A=( 12345, -1234, 3210) G=( 100, -50, 200)
```
- `A=` : 가속도 XYZ (int16, ±4g 풀스케일 → 1g ≈ 8192)
- `G=` : 자이로 XYZ (int16, ±2000dps 풀스케일 → 1dps ≈ 16.4)
- 디바이스 수평 정치 시 Z축 가속도 약 `+8192` 근처, 자이로 `0` 근처 (±수십 이내)
실패 패턴:
| 로그 | 원인 |
|---|---|
| `[IMU] FAIL — gyro config write (ret=-5)` | I2C TX 에러 |
| `[IMU] FAIL — data read (ret=-5)` | I2C RX 에러 |
| BLE로 `rsp: 0xFFFF` 수신 | 위 에러 발생 시 앱으로 전송되는 에러 응답 |
**3. BLE 응답 패킷 포맷**
```
[r][s][p][:] [AX_H][AX_L] [AY_H][AY_L] [AZ_H][AZ_L]
[GX_H][GX_L] [GY_H][GY_L] [GZ_H][GZ_L]
[CRC_L][CRC_H] = 18 bytes
```
### ❌ 미구현
| 모듈 | 목표 파일 | 필요 기능 |
|---|---|---|
| 피에조 드라이버 | `src/drivers/piezo/piezo.c` | SW burst (2.1MHz), MUX, 전원 제어 |
| Echo ADC | `src/drivers/echo_adc/echo_adc.c` | ADC121S051, SPIM3 @ 16MHz |
| `maa?` 커맨드 | `src/parser.c` 추가 | 피에조 6ch 단독 측정 → `reb:` ×6 + `raa:` |
| `mbb?` 커맨드 | `src/parser.c` 추가 | 전체 측정 오케스트레이션 → `rbb:` + `reb:` ×6 + `raa:` |
---
## 7. 핀 배치 전체 정리
| 신호 | 핀 | 방향 | 모듈 | 상태 |
|---|---|---|---|---|
| POWER_HOLD | P0.08 | OUT | 전원 래치 | ✅ |
| POWER_BTN | P1.08 | IN | 전원 버튼 | ✅ |
| LED_BLE | P0.12 | OUT | 파란 LED | ✅ |
| LED_FUNC | P0.29 | OUT | 주황 LED | ✅ |
| BATT_ADC | P0.04 (AIN2) | AIN | 배터리 ADC | ✅ |
| IMU_SCL | P1.14 | I2C | ICM42670P | ✅ |
| IMU_SDA | P1.15 | I2C | ICM42670P | ✅ |
| TEMP_ADC | P0.05 (AIN3) | AIN | TMP235-Q1 | 🔲 |
| PIEZO_PWR_EN | P1.09 | OUT | DC/DC +/-20V | ❌ |
| PIEZO_PE | P0.25 | OUT | Pulse Enable | ❌ |
| PIEZO_P_OUT | P1.07 | OUT | 양극 출력 | ❌ |
| PIEZO_N_OUT | P1.06 | OUT | 음극 출력 | ❌ |
| PIEZO_DMP | P1.00 | OUT | Dump | ❌ |
| MUX_EN_A | P0.21 | OUT | MUXA 활성화 | ❌ |
| MUX_EN_B | P0.23 | OUT | MUXB 활성화 | ❌ |
| MUX_SEL0 | P1.10 | OUT | MUX 채널 선택 | ❌ |
| MUX_SEL1 | P0.28 | OUT | MUX 채널 선택 | ❌ |
| ECHO_SCLK | P0.14 | SPI | ADC121S051 CLK | ❌ |
| ECHO_MISO | P0.15 | SPI | ADC121S051 DATA | ❌ |
| ECHO_CS | P0.19 | OUT | ADC121S051 CS | ❌ |
---
## 8. IMU 테스트 가이드 (msp?)
### 부팅 시 확인 (RTT 로그)
부팅 직후 `[1] HW Init` 단계에서 아래 줄이 나와야 함:
```
[IMU] OK — ICM42670P detected (WHOAMI=0x67, addr=0x68)
```
#### 실패 패턴별 원인
| 로그 | 원인 | 조치 |
|---|---|---|
| `[IMU] FAIL — I2C bus not ready` | Zephyr I2C 드라이버 초기화 실패 | `CONFIG_I2C=y` 확인, overlay 확인 |
| `[IMU] FAIL — WHOAMI read error (check SCL=P1.14, SDA=P1.15)` | I2C 통신 자체 실패 | 핀 납땜/연결 확인, 풀업 저항 확인 |
| `[IMU] FAIL — WHOAMI mismatch (got=0x00, expected=0x67)` | 버스는 살아있으나 응답 이상 | I2C 주소(0x68) 확인, AD0 핀 상태 확인 |
---
### msp? 커맨드 전송 시 확인 (RTT 로그)
앱에서 `msp?` 전송 시 아래 형식으로 출력됨:
```
[IMU] msp: A=( 12345, -1234, 3210) G=( 100, -50, 200)
```
- `A=` : 가속도 XYZ (int16, ±4g 풀스케일 → 1g ≈ 8192)
- `G=` : 자이로 XYZ (int16, ±2000dps 풀스케일 → 1dps ≈ 16.4)
- 디바이스가 수평으로 놓여 있으면 Z축 가속도가 약 `+8192` 근처여야 함
- 자이로는 정지 상태에서 `0` 근처 (±수십 이내)
#### 실패 패턴
| 로그 | 원인 |
|---|---|
| `[IMU] FAIL — gyro config write (ret=-5)` | I2C TX 에러 (부팅 후 재연결 문제) |
| `[IMU] FAIL — data read (ret=-5)` | I2C RX 에러 |
| BLE로 `rsp: 0xFFFF` 수신 | 위 에러 발생 시 앱으로 전송되는 에러 응답 |
---
### BLE 응답 패킷 확인
정상 수신 시 앱에서 받는 `rsp:` 패킷 포맷:
```
[r][s][p][:] [AX_H][AX_L] [AY_H][AY_L] [AZ_H][AZ_L]
[GX_H][GX_L] [GY_H][GY_L] [GZ_H][GZ_L]
[CRC_L][CRC_H]
= 18 bytes
```
---
## 9. 다음 구현 순서
```
Step 1 ✅ 온도 센서 (tmp235.c) — 완료
Step 2 피에조 드라이버 (piezo.c)
└─ GPIO 초기화, MUX, SW burst (NOP 기반, nrf HAL 직접 사용)
└─ CONFIG_NRFX_GPIOTE=y 추가
Step 3 Echo ADC (echo_adc.c)
└─ SPIM3 초기화, 연속 샘플 캡처
└─ CONFIG_NRFX_SPIM3=y 추가
Step 4 maa? 커맨드 (parser.c 추가)
└─ 피에조 6ch 측정 + BLE 전송 (reb: ×6 + raa:)
└─ 센서 번들 없음 — 피에조/ADC 단독 검증용
Step 5 mbb? 커맨드 (parser.c 추가)
└─ 배터리 + IMU + 온도 + 피에조 6ch 전체 오케스트레이션
└─ rbb: + reb: ×6 + raa:
```