15 KiB
VesiScan-Basic — 시스템 전체 파이프라인
목차
- 전체 동작 개요
- 부팅 및 BLE 연결 흐름
- 커맨드/응답 테이블
- msp? 흐름 (1초 주기 IMU)
- mbb? 흐름 (10초 주기 전체 측정)
- 구현 현황
- 핀 배치 전체 정리
- IMU 테스트 가이드
- 다음 구현 순서
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: