Files
IR_test_firmware/doc

IR 광신호 Mock 프로토타입 설계 정리

대상 과제: IR 광신호 기반 무선 토양수분 센서 (농촌진흥청 국립원예특작과학원) 본 문서 범위: 센서 미개발 상태에서 온도·습도 mock 신호를 IR LED로 송출하고 카메라로 인식하는 벤치 프로토타입 작성 기준일: 2026-08-22


1. 프로토타입 목표

각 식물 팟에 온·습도 센서가 내장되고, 그 값을 IR LED 광신호로 변환해 외부 카메라가 판독하는 시스템을 검증한다. 실제 센서가 아직 없으므로 MCU가 결정적 패턴의 mock 값을 생성하여 송출하고, 카메라 측 디코더가 이를 정확히 복원하는지 확인하는 것이 이번 단계의 목적이다.

배터리 수명, 방수, 양산성은 본 단계에서 고려하지 않는다.


2. 확정된 설계 결정

항목 결정 근거
MCU 보드 NUCLEO-L073RZ (STM32L073RZ) 기 확보
LED Siemens LD274 (950nm) × 9 기 확보, 추후 변경 예정
카메라 필터 950nm, C 마운트 LD274 피크 파장과 정합
셔터 방식 글로벌 셔터 확인 완료
인코딩 공간 병렬 (3×3 동시) 벤치 환경이라 분리 해상 가능
구동 방식 GPIO 오픈 드레인 직결 근거리라 저전류로 충분
테스트 거리 1 ~ 1.5 m 블롭 분리 여유 확보

2.1 기본 카메라 필터 제거 건

카메라에 기본 장착돼 있던 필터(IR-cut 추정)를 제거하고, 별도 950nm 필터를 C 마운트로 장착했다. 제거한 기본 필터는 이후 설계와 무관하다.


3. LED 특성 (LD274) 및 그 함의

3.1 데이터시트 주요 값

항목
피크 파장 λ_peak 950 nm
스펙트럼 폭 (50%) 55 nm
반각 φ ±10°
방사강도 I_e (I_F = 100 mA) 최소 50 mW/sr
순방향 전압 V_F (I_F = 100 mA) 1.30 V (max 1.5 V)
최대 연속 순방향 전류 100 mA
패키지 5 mm (T-1 3/4), 지름 5.1 mm
스위칭 시간 t_r, t_f 1 µs

3.2 파장 정합 — 양호

950nm 필터와 LD274의 950nm 피크가 일치한다. 스펙트럼 폭 55nm를 감안해도 에너지가 대략 920~990nm에 몰려 있어 필터 통과에 문제가 없다.

3.3 반각 ±10° — 최대 리스크 요인

LD274는 TV 리모컨용으로 설계된 협각 부품이다. 방사 특성상 ±10°에서 50%로 떨어지고 약 15° 이후 급락한다.

거리별 50% 빔 지름:

거리 빔 지름
1 m 0.35 m
3 m 1.06 m
10 m 3.5 m

문제의 본질은 거리가 아니라 조준이다. 팟이 흩어져 있고 카메라가 한쪽에 있으면 팟마다 LED가 향해야 할 방향이 전부 다르다. LED를 모두 수직으로 세우면 카메라 바로 아래 팟만 인식된다.

벤치 테스트(카메라를 LED 정면에 배치)에서는 문제가 되지 않으나, 양산 부품 선정 시 반각을 1순위 기준으로 삼아야 한다. 파장과 전류는 나중에 맞출 수 있지만 반각은 고칠 수 없다. 권장: ±20~25° 급 광각 부품.

3.4 펄스 구동은 채택하지 않음

서지 3A, 펄스 내량상 D=0.05에서 1A까지 가능하나, 방사강도 그래프상 1A에서 광량은 100mA 대비 약 7배에 그친다(전류는 10배). 효율이 떨어진다.

카메라는 노출 시간 동안 에너지를 적분하므로, 같은 평균 전류라면 DC 구동이 유리하다. 펄스가 유리한 경우는 카메라 노출 창에 동기하여 구동할 때뿐인데, 프리러닝 카메라에서는 동기를 잡을 수 없다.

대안: 카메라 노출 시간을 늘린다. 33ms 프레임에서 노출을 1ms → 20ms로 늘리면 LED 에너지를 20배 적분한다. 950nm 필터가 주변광을 이미 억제하고 있다.


4. 하드웨어 구성

4.1 구동 전류 및 저항 산출

벤치 1m 환경에서는 오히려 과다 광량이 문제가 된다. 포화된 블롭이 번지면(blooming) 인접 LED와 뭉쳐 공간 병렬 인코딩이 깨진다.

목표 전류: 6 mA
V_F ≈ 1.1 V   (순방향 특성 그래프의 저전류 영역 판독값)
V_OL ≈ 0.4 V  (STM32L0 오픈 드레인 Low 출력)

R = (3.3 - 1.1 - 0.4) / 0.006 ≈ 300 Ω  →  330 Ω (E24)
  • 9개 합계: 약 54 mA
  • 밝기 부족 시 220 Ω으로 하향 (합계 약 72 mA)

거리별 조도 추정 (8 mA 기준, I_e ≈ 4 mW/sr):

거리 조도 판단
1 m 4 mW/m² 20ms 노출로 충분히 포화
3 m 0.44 mW/m² 가능, 노출 조정 필요
10 m 0.04 mW/m² 외부 드라이버 필요

결론: 벤치 범위에서는 MOSFET·ULN2803A 등 외부 드라이버가 불필요하다.

4.2 GPIO 전류 한계

STM32L073 GPIO는 정규 스펙에서 핀당 ±8 mA, 비정규 V_OL/V_OH를 감수하면 ±15 mA까지 sink/source 가능하다. 6 mA 구동은 정규 스펙 내에 안전하게 들어간다.

전체 I/O 합산 전류(ΣI_VSS) 한계는 데이터시트 절대최대정격 표에서 확인할 것.

4.3 전원 경로

LED 전류를 Nucleo의 3.3V 핀에서 뽑는 것은 무방하나(54mA 수준), 향후 전류를 크게 올릴 경우 주의가 필요하다.

  • ST-LINK USB 급전 시 열거 전에는 100 mA 제한
  • 온보드 LDO를 MCU와 공유

전류를 20mA/개 이상으로 올릴 경우 5V 핀 또는 별도 전원에서 공급하고 GND만 공유한다. 이때는 반드시 MOSFET 또는 ULN2803A를 경유해야 한다 — PC0~PC5는 ADC 겸용 핀이라 5V 톨러런트가 아니다.

4.4 LED 물리 배치

3×3 배열, 피치 10 mm

LD274 패키지 지름이 5.1 mm이므로 몸통 간 5 mm 간격이 확보된다.

1 m 거리, FHD, 6 mm 렌즈 기준 약 2 px/mm:

항목 픽셀
LED 중심 간 거리 (10 mm) 약 21 px
렌즈 돔 블롭 (5 mm) 약 10 px
블롭 사이 어두운 골 약 11 px

3 m로 물러나면 분리 7 px, 블롭 3.4 px로 제안서의 "3×3 픽셀 이상 투영" 조건을 겨우 만족한다. 벤치는 1~1.5 m 권장.

4.5 회로 요약

VDD 3.3V ──┬── LED(애노드) ... LED(캐소드) ── 330Ω ── PC0 (오픈 드레인)
           ├── LED ── 330Ω ── PC1
           ├── ...
           └── LED ── 330Ω ── PC8

오픈 드레인: 핀 Low = 전류 싱크 = 점등 / 핀 High = Hi-Z = 소등

5. 프로토콜 설계

5.1 비트 배치

위치 역할
중앙 PC0 토글 클록
상단 좌 PC1 온도 b3 (MSB)
상단 중 PC2 온도 b2
상단 우 PC3 온도 b1
중단 좌 PC4 온도 b0 (LSB)
중단 우 PC5 습도 b3 (MSB)
하단 좌 PC6 습도 b2
하단 중 PC7 습도 b1
하단 우 PC8 습도 b0 (LSB)

온도 4비트(16단계) + 습도 4비트(16단계) + 클록 1비트 = 9비트.

5.2 PC0의 역할: 토글 클록

글로벌 셔터가 확정되면서 롤링 셔터 대비용 동기 LED는 불필요해졌다. PC0을 심볼마다 반전하는 토글 클록으로 재배정한다.

검토했던 대안과 비교:

후보 얻는 것 잃는 것
토글 클록 (채택) 심볼 경계 검출, 누락 심볼 감지, 링크 생존 확인 고정 밝기 기준 없음
상시 점등 기준 자동 임계값 산출, ROI 앵커 경계·누락 미검출
패리티 단일 비트 오류 검출 위 둘 다 불가

채택 이유: 다수결 디코딩과 궁합이 좋다. 클록 전환 시점이 곧 심볼 경계이므로, 디코더는 전환 사이의 프레임만 모아 투표하면 되고 경계 프레임을 따로 판정할 필요가 없다.

밝기 임계값은 9개 중 켜진 LED들의 밝기 분포에서 얻을 수 있어 별도 기준 LED가 없어도 무방하다.

5.3 심볼 타이밍

심볼 주기 200 ms (30fps 기준 6프레임)

MCU와 카메라는 서로 독립적으로 동작한다. 노출 창 한가운데서 심볼이 바뀌면 전환된 LED들이 중간 밝기로 찍힌다. 글로벌 셔터라 9개가 균일하게 이상해질 뿐, 값 자체는 무효다.

심볼 N (200ms)          │ 심볼 N+1 (200ms)
[F][F][F][F][F]  [X]    │ [F][F][F][F][F]
                  ↑
          경계 걸침 프레임 1개만 폐기

최악의 경우에도 6프레임 중 5프레임이 유효하므로 다수결로 뽑으면 오류율이 사실상 0이 된다. 요구 갱신 주기가 시간당 1회임을 감안하면 초당 5심볼은 과도한 속도이며, 남는 속도를 전부 신뢰성으로 환산하는 설계다.


6. CubeMX 설정

6.1 클록

항목
RCC HSI16, PLL 미사용
SYSCLK 16 MHz

프로토타입이므로 LPTIM·RTC·저전력 모드는 설정하지 않는다.

6.2 GPIO (PC0 ~ PC8)

항목 설정값 이유
GPIO mode Output Open Drain 요구 사양
Pull-up/Pull-down No pull-up and no pull-down 풀업은 외부 LED+저항이 담당. 내부 풀업 시 소등 상태에서도 누설
Maximum output speed Low 저속 신호에 고속 슬루 불필요, EMI·소비전류 감소
GPIO output level High OD에서 High = Hi-Z = 소등. Low로 두면 부팅 즉시 9개 전부 점등

User Label:

PC0 → CLK
PC1 → T3    PC2 → T2    PC3 → T1    PC4 → T0
PC5 → H3    PC6 → H2    PC7 → H1    PC8 → H0

GPIO output level을 High로 두는 것이 가장 실수하기 쉬운 지점이다. 푸시풀 습관대로 Low를 넣으면 리셋 직후부터 LED가 켜진 채로 있게 된다.

6.3 PC0~PC8 선택 이유

  • 단일 포트 연속 배치BSRR 한 번의 쓰기로 9개를 완전히 동시에 전환 가능
  • 회피 대상 핀과 충돌 없음: PA13/PA14(SWD), PA2/PA3(ST-LINK VCP), PC13(B1 버튼), PC14/PC15(LSE), PA5(LD2)
  • NUCLEO-64 모포 헤더 실제 핀맵은 보드 문서에서 확인할 것

6.4 타이머

항목
타이머 TIM6
Prescaler 15999
Counter Period (ARR) 199
결과 주기 200 ms
NVIC TIM6 global interrupt 활성화

TIM6를 선택한 이유는 기본 타이머(Basic Timer)라 GPIO 대체 기능을 전혀 점유하지 않기 때문이다. PC6~PC8은 TIM3 채널과 겹치므로 TIM3는 사용하지 않는다.

6.5 UART

항목
USART2 Asynchronous, 115200 8N1
용도 송출값 로그 (ST-LINK VCP 경유)

이 로그가 핵심이다. MCU가 "지금 온도 N, 습도 M을 송출했다"를 시리얼로 찍어주면 카메라 디코더 결과와 대조하여 에러율을 정량화할 수 있다. 이것이 없으면 디코더 오류인지 광학계 오류인지 구분할 수 없다.


7. 펌웨어

#define LED_MASK   0x01FFu
#define BIT_CLK    0x0001u          /* PC0 */

static uint8_t clk_state = 0;
static uint8_t mock_t = 0, mock_h = 0;

static void emit(uint8_t t4, uint8_t h4, uint8_t clk)
{
    uint16_t bits = 0;
    if (clk) bits |= BIT_CLK;
    bits |= (uint16_t)(t4 & 0x0F) << 1;   /* PC1..PC4 */
    bits |= (uint16_t)(h4 & 0x0F) << 5;   /* PC5..PC8 */

    /* 오픈 드레인: 0 = 점등이므로 반전. 9핀 원자적 동시 전환 */
    GPIOC->BSRR = ((uint32_t)(bits & LED_MASK) << 16)
                | ((uint32_t)(~bits & LED_MASK));
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance != TIM6) return;

    clk_state ^= 1;
    emit(mock_t, mock_h, clk_state);

    printf("T=%2u H=%2u CLK=%u\r\n", mock_t, mock_h, clk_state);

    /* 결정적 스윕: 온도는 매 심볼, 습도는 16심볼마다 */
    mock_t = (mock_t + 1) & 0x0F;
    if (mock_t == 0) mock_h = (mock_h + 1) & 0x0F;
}

7.1 BSRR 직접 사용

HAL_GPIO_WritePin()을 9번 호출하면 핀마다 수십 ns씩 어긋나고 함수 호출 오버헤드가 붙는다. BSRR 직접 쓰기는 원자적 1사이클 연산이다.

7.2 Mock 값은 난수가 아닌 결정적 스윕

온도가 매 심볼 1씩 증가하므로, 디코더가 0, 1, 2, 4, 5를 출력하면 3을 놓쳤다는 사실이 즉시 드러난다. 난수 mock으로는 알 수 없는 정보다.

  • 전체 주기: 온도 16 × 습도 16 = 256 심볼 = 51.2초
  • 1분 녹화로 전 조합 커버리지 확보

8. 카메라 설정 — 반드시 수동 고정할 항목

여기서 실패하는 경우가 많다.

항목 조치 이유
자동 노출 (AE) 끄기 LED 점멸마다 노출이 요동쳐 비트 패턴에 따라 밝기가 달라지고 임계값이 무너짐
자동 게인 (AGC) 끄기 동일
자동 화이트밸런스 끄기 950nm 단색이라 무의미하고 방해만 됨
프레임레이트 자동 조정 끄기 어두우면 자동으로 15fps로 떨어지는 카메라가 많음
노출 시간 20 ms 부근에서 시작 후 튜닝 블롭 중심만 포화되고 번지지 않는 지점을 탐색

노출 튜닝이 이번 벤치 테스트에서 가장 시간이 많이 소요될 지점이다.


9. 브링업 절차

  1. LED 1개(PC0)만 330Ω으로 연결 → 스마트폰 카메라로 점등 확인 (필터 없이)
  2. 950nm 필터 장착 → 실제 카메라로 블롭 확인, 노출 튜닝
  3. 9개 전부 연결 → 모든 비트를 수동으로 켜서 9개 블롭이 분리되는지 확인
  4. TIM6 활성화, 스윕 시작 → USART 로그와 디코더 출력 대조

3번에서 블롭이 뭉치면 피치를 넓히거나 카메라 거리를 조정한다. 이 단계가 통과되지 않으면 디코더를 아무리 수정해도 소용없다.


10. 제안서 수정 필요 항목

프로토타입 완료 후 문서 갱신 시 놓치기 쉬운 지점들.

위치 현재 기재 수정 방향
2. 다. 4단계 850nm 파장의 IR LED 950nm
3. 가. 광출력 발광부 고효율 850nm IR LED 950nm
4. 가. 신호 발광 소자 850nm 고출력 IR 광 LED 950nm
4. 나. 수집 카메라 사양 850nm 밴드패스 필터 일체형 950nm 필터, C 마운트 별도 장착
6. BOM 2번 850nm 파장 5mm 외형 950nm
2. 다. 1단계 Stop2 초저전력 슬립 모드 Stop2는 STM32L4 계열 용어. L0는 Stop 모드

Stop 모드 관련 보충: STM32L0의 Stop 모드는 RTC 유지 시 수 µA대로, 제안서에 기재된 5µA보다 오히려 유리하다. 수치는 문제없으나 모드 명칭이 발주처 기술 검토에서 지적될 수 있다.


11. 본 프로토타입 이후 결정할 사항

11.1 LED 부품 교체 기준 (우선순위 순)

  1. 반각 — ±20~25° 이상. 이것이 실제 설치 가능성을 좌우함
  2. 파장 — 필터와 정합 (현 필터 유지 시 940~950nm)
  3. 방사강도 — 목표 거리에서의 조도로 역산
  4. 패키지 — 3×3 배치 시 피치 확보 가능한 크기

11.2 미결 항목

  • 온도 4비트(16단계)로 충분한가 — 제안서의 "수분 14단계"와는 정합하나 온도는 부족할 수 있음. 온도 5비트 + 습도 4비트로 가려면 심볼 2개로 분할하거나 LED 수를 늘려야 함
  • 실제 목표 인식 거리 — 벤치 1m 검증 후 온실 실환경 거리 확정 필요. 3m 초과 시 외부 드라이버 및 광각 LED 필수
  • 팟 배치와 카메라 위치 기하 — 조준 문제의 실제 발생 여부는 이 기하에 달려 있음
  • 디코더 구현 — ROI 자동 검출, 클록 기반 심볼 분할, 다수결 로직 (Python)

부록: 핵심 수치 요약

LED          : LD274, 950nm, 반각 ±10°, V_F ≈ 1.1V @ 6mA
저항          : 330Ω (대안 220Ω)
구동 전류      : 6 mA/개, 합계 54 mA
배치          : 3×3, 피치 10mm
거리          : 1 ~ 1.5 m
심볼 주기      : 200 ms (6 프레임 @ 30fps)
유효 프레임    : 심볼당 최소 5
전체 스윕 주기 : 51.2 초 (256 심볼)
MCU          : STM32L073RZ, HSI16, TIM6 (PSC 15999 / ARR 199)
GPIO         : PC0~PC8, Open Drain, No pull, Low speed, Output level High
로그          : USART2 115200 8N1