# VivaMyo BLE Application - 프로그램 아키텍처 문서

## 1. 프로젝트 개요

VivaMyo는 nRF52840 기반 BLE 의료 기기 펌웨어로, 방광 모니터링을 위한 NIRS(Near-Infrared Spectroscopy) 장치입니다.

### 1.1 주요 특징
- **MCU**: Nordic nRF52840
- **통신**: BLE (Nordic UART Service)
- **보안**: LESC 페어링, Static Passkey
- **센서**: LED 48개, Photodetector, IMU (ICM42670P), 온도센서, 압력센서
- **저장**: EEPROM (설정값 암호화 저장)

---

## 2. 시스템 아키텍처

```
┌─────────────────────────────────────────────────────────────────────┐
│                          main.c                                      │
│                    (Application Entry Point)                         │
└──────────────────────────────┬──────────────────────────────────────┘
                               │
        ┌──────────────────────┼──────────────────────┐
        ▼                      ▼                      ▼
┌───────────────┐    ┌─────────────────┐    ┌─────────────────────┐
│   BLE Stack   │    │  Power Control  │    │   Timer Management  │
│  (ble_core.c) │    │ (power_ctrl.c)  │    │   (main_timer.c)    │
└───────┬───────┘    └─────────────────┘    └─────────────────────┘
        │
        ▼
┌───────────────────────────────────────────────────────────────────┐
│                        nus_data_handler()                          │
│                     (BLE Data Reception)                           │
└──────────────────────────────┬────────────────────────────────────┘
                               │
                               ▼
┌───────────────────────────────────────────────────────────────────┐
│                   received_command_process()                       │
│                        (cmd_parse.c)                               │
└──────────────────────────────┬────────────────────────────────────┘
                               │
        ┌──────────────────────┴──────────────────────┐
        ▼                                             ▼
┌───────────────────────┐                   ┌─────────────────────┐
│     Mt_parser         │                   │   Legacy Parser     │
│   (dr_cmd_parser)     │                   │   (cmd_parse.c)     │
│                       │                   │                     │
│  parser.c + cmd.c     │                   │  기존 명령어 처리   │
└───────────┬───────────┘                   └─────────────────────┘
            │
            ▼
┌───────────────────────────────────────────────────────────────────┐
│                     Command Handlers (cmd.c)                       │
│                                                                    │
│  Cmd_mta, Cmd_sta, Cmd_mcj, Cmd_msn, Cmd_mag ...                  │
└───────────────────────────────────────────────────────────────────┘
```

---

## 3. Mt_parser 모듈 구조

Mt_parser는 명령어 파싱과 디스패치를 담당하는 독립 모듈입니다.

### 3.1 파일 구성

| 파일 | 경로 | 설명 |
|------|------|------|
| `parser.h` | `/mt_parser/parser.h` | 파서 인터페이스 정의 |
| `parser.c` | `/mt_parser/parser.c` | CRC 검증, TAG 추출, 명령어 디스패치 |
| `cmd.h` | `cmd/cmd.h` | 명령어 테이블 구조체 정의 |
| `cmd.c` | `cmd/cmd.c` | 명령어 핸들러 구현 및 테이블 |
| `dr_util.h/c` | `/mt_parser/dr_util/` | BLE 응답 유틸리티 함수 |

### 3.2 핵심 구조체

```c
// parser.h - 플랫폼 인터페이스
typedef struct {
    void (*log)(const char *fmt, ...);      // 로그 출력 함수
    void (*tx_bin)(const uint8_t *buf, uint16_t len);  // BLE 전송 함수
    bool crc_check;                          // CRC 검사 활성화 여부
} dr_platform_if_t;

// cmd.h - 파싱된 명령어 구조체
typedef struct {
    char    tag[5];               // 4글자 명령어 + NULL ("sta?")
    uint8_t data[CMD_MAX_DATA];   // TAG 이후 데이터
    uint8_t data_len;             // 데이터 길이
} ParsedCmd;

// cmd.h - 명령어 테이블 엔트리
typedef struct {
    char tag[5];                  // 명령어 TAG ("sta?")
    bool enabled;                 // 활성화 여부
    cmd_handler_t handler;        // 핸들러 함수 포인터
} CmdEntry;
```

### 3.3 전역 변수

```c
extern dr_platform_if_t g_plat;   // 플랫폼 인터페이스
extern bool g_log_enable;         // 로그 활성화 플래그
extern CmdEntry g_cmd_table[];    // 명령어 테이블
extern const uint16_t g_cmd_count; // 명령어 개수
```

---

## 4. 명령어 처리 흐름

### 4.1 데이터 수신 경로

```
[BLE Central]
    │
    ▼ (NUS RX Characteristic Write)
[ble_core.c:nus_data_handler()]
    │
    ▼
[cmd_parse.c:received_command_process(data, CMD_BLE, length)]
    │
    ├─► [Mt_parser 초기화] (최초 1회)
    │   g_plat.log = log_printf
    │   g_plat.tx_bin = binary_tx_handler
    │   g_plat.crc_check = true
    │
    ▼
[parser.c:dr_cmd_parser(data, length)]
    │
    ├─► [CRC16 검증] → 실패시 "crc!" 응답
    │
    ├─► [TAG 추출] → 4글자 명령어 파싱
    │
    ▼
[dr_cmd_dispatch()] → g_cmd_table 검색
    │
    ├─► [명령어 발견 + 활성화] → handler() 호출
    │
    ├─► [명령어 발견 + 비활성화] → return 0
    │
    └─► [명령어 미발견] → return 0 (Legacy 파서로 fallback)
```

### 4.2 CRC16 검증

```c
// 패킷 구조: [TAG(4)] [DATA(N)] [CRC_LO] [CRC_HI]
// CRC 계산: TAG + DATA 영역
// CRC 위치: 패킷 마지막 2바이트 (Little Endian)

static bool dr_crc16_check_packet(const uint8_t *packet, uint32_t packet_len)
{
    uint32_t data_len = packet_len - 2;
    uint16_t expected_crc = (uint16_t)packet[packet_len - 2]
                          | ((uint16_t)packet[packet_len - 1] << 8);
    return dr_crc16_check(packet, data_len, expected_crc);
}
```

---

## 5. 명령어 테이블 (cmd.c)

### 5.1 명령어 카테고리

| 카테고리 | 명령어 | 설명 |
|----------|--------|------|
| **A. Device Status** | `mta?`, `sta?`, `str?`, `mqq?` | 디바이스 상태 제어/조회 |
| **B. AGC/Gain** | `mag?`, `sag?`, `sar?` | Auto Gain Control |
| **C. LED DP Value** | `ssa?`, `sab?`, `ssb?`, `srb?` | LED 밝기 설정/조회 |
| **D. LED On/Off** | `ssc?`, `ssd?`, `sse?`, `ssf?`, `sif?`, `ssg?` | LED/Gain 제어 |
| **E. Simple PD** | `ssh?` | 간단한 PD 측정 |
| **F. M48 Full** | `mcj?`, `scj?`, `sdj?`, `sej?`, `sfj?`, `ssj?`, `szj?` | 48채널 PD 측정 |
| **G. IMM ADC** | `saj?` | Immediate ADC 측정 |
| **H. PD Settings** | `ssk?`, `srk?`, `ssl?`, `srl?` | ADC 횟수/지연 설정 |
| **I. Sensors** | `msn?`, `ssn?`, `spn?`, `sso?`, `ssp?` | 배터리/압력/온도/IMU |
| **J. System** | `ssq?`, `ssr?`, `sss?`, `sst?`, `ssv?` | 전원/리셋/버전 |
| **K. Serial/Passkey** | `ssz?`, `spz?`, `sqz?`, `srz?` | 시리얼번호/패스키 |
| **L. EEPROM Array** | `sez?`, `sfz?`, `sgz?` | EEPROM 배열 R/W |
| **M. HW/Life Cycle** | `siz?`, `shz?`, `sxz?`, `syz?` | HW번호/사용횟수 |
| **N. Debug** | `cmd?` | GPIO 테스트 |

### 5.2 명령어 상세 테이블

```c
CmdEntry g_cmd_table[] = {
    /* Debug */
    { "cmd?", true,  Cmd_cmd },   // GPIO 테스트

    /* A. Device Status */
    { "mta?", true,  Cmd_mta },   // Device Activate/Sleep (신규)
    { "sta?", true,  Cmd_sta },   // Device Activate/Sleep (호환)
    { "str?", false, Cmd_str },   // Status Read
    { "mqq?", true,  Cmd_mqq },   // Quick Measurement

    /* B. AGC / Gain Measurement */
    { "mag?", true,  Cmd_mag },   // Full AGC (신규)
    { "sag?", true,  Cmd_mag },   // Full AGC (호환)
    { "sar?", false, Cmd_sar },   // Read LED-PD Gain Array

    /* C. LED Power / DP Value */
    { "ssa?", false, Cmd_ssa },   // Single LED Power Read
    { "sab?", false, Cmd_sab },   // All LED AGC Data Read
    { "ssb?", false, Cmd_ssb },   // Single LED Power Write
    { "srb?", false, Cmd_srb },   // Read All 48 LED DP

    /* D. LED On/Off & Gain Control */
    { "ssc?", false, Cmd_ssc },   // LED On/Off (index 0-47, 99=off)
    { "ssd?", false, Cmd_ssd },   // AGC Switch On/Off
    { "sse?", false, Cmd_sse },   // Measure DAC Voltage
    { "ssf?", false, Cmd_ssf },   // Set LED DAC Value
    { "sif?", false, Cmd_sif },   // Immediate Gain Set
    { "ssg?", false, Cmd_ssg },   // Set PD Channel

    /* E. Simple PD Measurement */
    { "ssh?", false, Cmd_ssh },   // Simple PD Measurement

    /* F. PD-ADC M48 Full Measurement */
    { "mcj?", true,  Cmd_mcj },   // MODE=2 (Pressure + M48) - 신규
    { "scj?", true,  Cmd_mcj },   // MODE=2 (호환)
    { "sdj?", false, Cmd_sdj },   // MODE=3 (M48 Only)
    { "sej?", false, Cmd_sej },   // MODE=4 (M48 + Batt + IMU)
    { "sfj?", false, Cmd_sfj },   // MODE=5 (M48 + Init)
    { "ssj?", false, Cmd_ssj },   // MODE=0 (Combined)
    { "szj?", false, Cmd_szj },   // FAST Mode Settings

    /* G. IMM ADC */
    { "saj?", false, Cmd_saj },   // 4-LED Immediate ADC

    /* H. PD-ADC Count & Delay */
    { "ssk?", false, Cmd_ssk },   // Set ADC Count (8/16/24/32)
    { "srk?", false, Cmd_srk },   // Read ADC Count
    { "ssl?", false, Cmd_ssl },   // Set PD Delay (us)
    { "srl?", false, Cmd_srl },   // Read PD Delay

    /* I. Sensor Measurements */
    { "msn?", true,  Cmd_msn },   // Battery Level (신규)
    { "ssn?", true,  Cmd_msn },   // Battery Level (호환)
    { "spn?", false, Cmd_spn },   // Pressure Measurement
    { "sso?", false, Cmd_sso },   // Temperature Measurement
    { "ssp?", false, Cmd_ssp },   // IMU Raw Data

    /* J. Power / Reset / Version */
    { "ssq?", false, Cmd_ssq },   // Power Off
    { "ssr?", false, Cmd_ssr },   // Bond Delete + Reset
    { "sss?", false, Cmd_sss },   // Device Reset
    { "sst?", false, Cmd_sst },   // Ready Response
    { "ssv?", false, Cmd_ssv },   // Firmware Version

    /* K. Serial / Passkey */
    { "ssz?", false, Cmd_ssz },   // Write Serial Number
    { "spz?", false, Cmd_spz },   // Write Passkey
    { "sqz?", false, Cmd_sqz },   // Read Passkey
    { "srz?", false, Cmd_srz },   // Read Serial Number

    /* L. EEPROM Array */
    { "sez?", false, Cmd_sez },   // Write 48*uint16 Array
    { "sfz?", false, Cmd_sfz },   // Read 48*uint16 Array
    { "sgz?", false, Cmd_sgz },   // Load DP Preset

    /* M. Hardware No / Life Cycle */
    { "siz?", false, Cmd_siz },   // Read HW Number
    { "shz?", false, Cmd_shz },   // Write HW Number
    { "sxz?", false, Cmd_sxz },   // Write Life Cycle
    { "syz?", false, Cmd_syz },   // Read Life Cycle
};
```

### 5.3 활성화된 명령어 (enabled=true)

| 명령어 | 핸들러 | 기능 |
|--------|--------|------|
| `cmd?` | `Cmd_cmd` | GPIO 핀 제어 테스트 |
| `mta?` | `Cmd_mta` | 디바이스 활성화/슬립 |
| `sta?` | `Cmd_sta` | 디바이스 활성화/슬립 (호환) |
| `mqq?` | `Cmd_mqq` | 빠른 측정 시작 |
| `mag?` | `Cmd_mag` | Full AGC 측정 |
| `sag?` | `Cmd_mag` | Full AGC 측정 (호환) |
| `mcj?` | `Cmd_mcj` | M48 전체 측정 (MODE=2) |
| `scj?` | `Cmd_mcj` | M48 전체 측정 (호환) |
| `msn?` | `Cmd_msn` | 배터리 레벨 측정 |
| `ssn?` | `Cmd_msn` | 배터리 레벨 측정 (호환) |

---

## 6. main.c와의 연결

### 6.1 초기화 순서

```c
int main(void)
{
    // 1. 기본 초기화
    uart_handler_init();     // UART 디버그
    log_init();              // NRF 로그
    gpio_init();             // GPIO 설정 (LED, PD, GAIN_SW)
    timers_init();           // 타이머 초기화

    // 2. 설정 로드
    load_device_configuration();  // EEPROM에서 설정 읽기

    // 3. BLE 초기화
    ble_stack_init();        // SoftDevice 활성화
    gap_params_init();       // GAP 파라미터 (디바이스명, Passkey)
    gatt_init();             // GATT 초기화
    services_init();         // NUS 서비스 등록
    advertising_init();      // 광고 설정
    conn_params_init();      // 연결 파라미터

    // 4. 전원 버튼 타이머 시작
    power_ctrl_timers_start();

    // 5. 메인 루프
    for (;;) {
        idle_state_handle();  // 전원 관리 + 보안 처리
    }
}
```

### 6.2 BLE 데이터 수신 경로

```c
// ble_core.c
static void nus_data_handler(ble_nus_evt_t * p_evt)
{
    if (p_evt->type == BLE_NUS_EVT_RX_DATA) {
        // cmd_parse.c의 received_command_process() 호출
        received_command_process(
            p_evt->params.rx_data.p_data,
            CMD_BLE,
            p_evt->params.rx_data.length
        );
    }
}
```

### 6.3 Mt_parser 초기화 (cmd_parse.c)

```c
void received_command_process(uint8_t const *data_array, which_cmd_t cmd_t, uint8_t length)
{
    // Mt_parser 초기화 (최초 1회)
    static bool parser_initialized = false;
    if (!parser_initialized) {
        g_plat.log = log_printf;           // 로그 함수 연결
        g_plat.tx_bin = binary_tx_handler; // BLE TX 함수 연결
        g_plat.crc_check = true;           // CRC 검사 활성화
        g_log_enable = true;
        parser_initialized = true;
    }

    // Mt_parser 호출
    int result = dr_cmd_parser(r_data, length);

    if (result > 0) {
        return;  // Mt_parser에서 처리됨
    }

    // Mt_parser에서 처리 안됨 → Legacy 파서로 처리
    // ... 기존 scmd 기반 처리 ...
}
```

---

## 7. 소스코드 구조

### 7.1 디렉토리 구조

```
ble_app_vivaMayo/
├── main.c                    # 메인 엔트리 포인트
├── main.h                    # 메인 헤더 (타입 정의, 함수 프로토타입)
├── main_timer.c/h            # 애플리케이션 타이머
│
├── cmd_parse.c/h             # 명령어 파싱 (Legacy + Mt_parser 연결)
│
├── cmd/
│   ├── cmd.c                 # 명령어 핸들러 구현 + 테이블
│   └── cmd.h                 # 명령어 구조체 정의
│
├── ble/
│   ├── ble_core.c/h          # BLE 스택, GAP, GATT, 광고
│   ├── ble_data_tx.c/h       # BLE 데이터 전송, 포맷팅
│   ├── ble_services.c/h      # BLE 서비스
│   └── ble_security.c/h      # BLE 보안 (페어링)
│
├── power/
│   └── power_ctrl.c/h        # 전원 제어, 슬립 모드
│
├── peripheral/
│   └── uart_handler.c/h      # UART 디버그 출력
│
├── config/
│   └── device_config.c/h     # EEPROM 설정 관리
│
├── measurements.c/h          # 측정 유틸리티
├── meas_pd_*.c/h             # PD ADC 측정 모듈들
├── full_agc.c/h              # AGC 자동 조절
├── battery_saadc.c/h         # 배터리 ADC
│
├── icm42670p/                # IMU 드라이버
├── i2c_manager.c/h           # I2C 관리
├── ada2200_spi.c/h           # SPI 디바이스
├── mcp4725_i2c.c/h           # DAC 제어
├── tmp235_q1.c/h             # 온도 센서
├── cat_interface.c/h         # CAT (압력센서) 인터페이스
│
└── docs/
    └── PROGRAM_ARCHITECTURE.md  # 이 문서
```

### 7.2 Mt_parser 디렉토리 (별도 위치)

```
/mt_parser/
├── parser.h                  # 파서 인터페이스
├── parser.c                  # 파서 구현 (CRC, TAG, 디스패치)
├── cmd.h                     # (참조용)
│
└── dr_util/
    ├── dr_util.h             # BLE 응답 유틸리티
    └── dr_util.c             # dr_ble_return_1/2/3()
```

---

## 8. 명령어 프로토콜

### 8.1 패킷 구조

```
┌─────────┬──────────────────┬─────────┐
│  TAG    │      DATA        │   CRC   │
│ 4 bytes │    N bytes       │ 2 bytes │
└─────────┴──────────────────┴─────────┘

예시: sta? 명령어로 디바이스 활성화
TX: [73 74 61 3F] [00 01] [CRC_LO] [CRC_HI]
     s  t  a  ?    value=1

응답: rta: value
RX: [72 74 61 3A] [00 01] [CRC_LO] [CRC_HI]
     r  t  a  :    value=1
```

### 8.2 데이터 형식

| 형식 | 설명 | 바이트 순서 |
|------|------|-------------|
| uint16 | 16비트 정수 | Big Endian (MSB first) |
| ASCII | 문자열 | 순차적 |
| CRC16 | 체크섬 | Little Endian (LSB first) |

### 8.3 응답 규칙

| 요청 TAG | 응답 TAG | 예시 |
|----------|----------|------|
| `sta?` | `rta:` | 상태 응답 |
| `mcj?` | (측정 데이터) | 멀티 패킷 |
| 오류 | `xxx!` | `crc!`, `err!` |

---

## 9. 주요 핸들러 구현 예시

### 9.1 Cmd_mta (디바이스 활성화)

```c
static int Cmd_mta(const ParsedCmd *cmd)
{
    uint16_t mode = 0;
    resetCount = 0;

    // 데이터에서 mode 추출 (word index 0)
    (void)cmd_get_u16(cmd, 0, &mode);

    if (mode == 1) {
        // 디바이스 활성화
        if (device_activated() == 0) {
            device_status = true;
        }
    }
    else if (mode == 0) {
        // 슬립 모드 진입
        if (device_status == true) {
            if (device_sleep_mode() == 0) {
                device_status = false;
            }
        }
    }

    // BLE 응답 전송
    if (g_plat.tx_bin) {
        single_format_data(ble_bin_buffer, "rta:", mode);
        binary_tx_handler(ble_bin_buffer, 3);
    }

    return 1;
}
```

### 9.2 Cmd_mcj (M48 전체 측정)

```c
static int Cmd_mcj(const ParsedCmd *cmd)
{
    (void)cmd;

    // 디바이스 활성화 확인
    if (device_status != true) {
        param_error("mcj?");
        return 1;
    }

    // 측정 모드 설정
    ADC_PD_MODE = 2;
    info4 = true;
    ble_got_new_data = false;
    processing = true;

    // 압력 측정
    pressure_all_level_meas();

    // AGC 스위치 OFF
    AGC_GAIN_SW(false);

    // M48 ADC 시작
    m48_samples_in_buffer = m_pd_adc_cnt;
    pd_adc_m48_start = true;

    // 타이머 시작
    battery_timer_stop();
    go_batt = true;
    motion_data_once = true;
    main_timer_start();

    return 1;
}
```

---

## 10. 에러 코드

| 코드 | 값 | 설명 |
|------|-----|------|
| `err_code1` | 65535 | 길이 오류 |
| `err_code2` | 65534 | 활성화 오류 |
| `err_code3` | 65533 | 파라미터 오류 |
| `err_code4` | 65532 | '?' 누락 |
| `err_code5` | 65531 | 알 수 없는 명령어 |
| `err_code6` | 65530 | CRC 오류 |

---

## 11. EEPROM 주소 맵

| 주소 | 크기 | 내용 |
|------|------|------|
| 0x0010 | 12 bytes | HW_NO (암호화) |
| 0x0020 | 6 bytes | Passkey (암호화) |
| 0x0030 | 12 bytes | SERIAL_NO (암호화) |
| 0x0060 | 1 byte | bond_data_delete |
| 0x0065 | 1 byte | reset_status |
| 0x0070 | 1 byte | m_pd_adc_cnt |
| 0x0080 | 2 bytes | m_pd_delay_us |
| 0x0090 | 4 bytes | m_life_cycle |
| 0x0480 | 96 bytes | led_pd_dac_v[48] (AGC Gain) |

---

## 12. 버전 정보

- **펌웨어 버전**: FW25LIT2B102
- **디바이스명**: MEDIDEV_2004
- **문서 작성일**: 2026-01-30
- **작성자**: Claude Assistant

---

## 13. 관련 문서

- [W25Q32RV_FLASH_MEMORY.md](W25Q32RV_FLASH_MEMORY.md) - Flash 메모리 스펙
- Nordic nRF52840 Datasheet
- Nordic SDK 17.x Documentation
