아두이노 센서 읽기에 인터럽트가 항상 필요하지 않은 이유

profile_image
작성자 펌웨어읽는도윤
댓글 0건 조회 8회

버튼을 눌렀는데 반응이 늦거나 센서 펄스를 놓치면 곧바로 인터럽트를 떠올리기 쉽습니다. 하지만 아두이노 센서 연결에서 인터럽트가 언제나 더 빠르고 정확한 선택은 아닙니다. 주기적으로 상태를 확인하는 폴링은 단순해서 문제가 생긴 위치를 찾기 쉽고, 인터럽트는 짧은 사건을 놓치지 않는 대신 실행 흐름과 공유 데이터 관리가 까다롭습니다.

핵심은 어느 방식이 고급 기술인지가 아니라 센서 신호가 얼마나 짧은지, 응답 지연을 어디까지 허용하는지, 메인 루프가 얼마나 바쁜지를 따지는 것입니다. 이 글에서는 폴링과 인터럽트를 대결 구도로 살펴보고, 실제 센서 노드 한 대를 설계하는 과정까지 따라가며 선택 기준을 구체화합니다.

폴링 vs 인터럽트, 빠르다는 말부터 다시 따져야 합니다

폴링은 기다리는 방식이 아니라 반복해서 확인하는 방식입니다

폴링은 loop()에서 입력 핀이나 센서 레지스터를 일정한 간격으로 읽는 구조입니다. 예를 들어 수위 스위치를 10ms마다 확인한다면 이론적인 감지 지연은 최대 약 10ms이며, 평균적으로는 그 절반 부근입니다. 사람이 버튼을 누르거나 탱크의 수위가 변하는 상황에서는 이 정도 차이를 체감하기 어렵습니다. 오히려 모든 처리가 한 흐름 안에 놓여 코드 순서를 눈으로 추적하기 쉽다는 장점이 큽니다.

인터럽트는 지정한 핀의 상승·하강 에지나 타이머 사건이 발생했을 때 현재 작업을 잠시 멈추고 서비스 루틴을 실행합니다. 수십 마이크로초밖에 유지되지 않는 회전 센서 펄스처럼 폴링 사이에 나타났다 사라질 신호에는 강합니다. 그러나 인터럽트에 진입하고 복귀하는 데도 비용이 들며, 사건이 지나치게 자주 발생하면 메인 루프가 실행될 시간을 빼앗습니다. 따라서 인터럽트는 무조건 빠른 코드가 아니라 짧은 사건을 우선 처리하는 구조라고 이해해야 합니다.

두 방식의 승부는 CPU 속도만으로 결정되지 않습니다. 센서의 출력 형태와 시스템의 허용 지연을 먼저 수치로 적어 보세요. 아두이노의 기본 개념과 활용 범위가 낯설다면 지식백과의 아두이노 설명을 함께 참고하면 보드와 입출력 구조를 이해하는 데 도움이 됩니다.

  • 폴링이 유리한 신호: 온도, 습도, 조도, 수위처럼 변화가 느리고 10~1000ms 정도의 지연을 허용하는 값
  • 인터럽트가 유리한 신호: 엔코더 펄스, 유량계 펄스, 웨이크업 핀처럼 지속 시간이 짧거나 발생 횟수 자체가 데이터인 값
  • 먼저 계산할 항목: 최소 펄스 폭, 최대 발생 빈도, 허용 응답 시간, 한 번의 센서 읽기에 걸리는 시간
  • 주의할 항목: 라이브러리가 내부적으로 인터럽트나 타이머를 이미 사용하는지 여부
실무 팁: “놓치면 안 된다”는 표현만으로 인터럽트를 선택하지 마세요. 놓칠 수 있는 최소 펄스 폭과 현재 루프의 최악 실행 시간을 마이크로초 또는 밀리초로 측정해야 판단이 선명해집니다.

구현 난도 대결에서는 단순한 폴링이 쉽게 이깁니다

인터럽트 서비스 루틴에는 보이지 않는 제한이 많습니다

폴링 코드는 센서 읽기, 조건 판단, 출력 갱신을 순서대로 배치할 수 있습니다. 시리얼 로그도 필요한 위치에 넣기 쉽고 지역 변수의 수명도 명확합니다. 반면 인터럽트 서비스 루틴, 즉 ISR에서는 오래 걸리는 작업을 피해야 합니다. 지연 함수, 복잡한 문자열 처리, 블로킹 통신, 빈번한 시리얼 출력 등을 ISR 안에서 실행하면 다른 인터럽트가 밀리거나 시스템 시간이 예상과 다르게 움직일 수 있습니다.

특히 I2C 통신 센서를 ISR 안에서 곧바로 읽는 설계는 피하는 편이 안전합니다. 많은 아두이노 환경에서 I2C 라이브러리는 하드웨어 인터럽트와 상태 전이를 이용하며, ISR 내부에서 다시 통신을 시작하면 완료를 기다리는 동안 진행 조건이 충족되지 않을 수 있습니다. 더 나은 구조는 인터럽트에서 플래그나 카운터만 바꾸고, 실제 센서 레지스터 읽기와 계산은 메인 루프가 담당하게 만드는 것입니다.

공유 변수에도 규칙이 생깁니다. ISR과 메인 코드가 함께 접근하는 값에는 보통 volatile이 필요하지만, 이 키워드가 원자적 접근까지 보장하지는 않습니다. 8비트 MCU에서 여러 바이트로 구성된 카운터를 읽는 도중 인터럽트가 값을 갱신하면 상·하위 바이트가 서로 다른 시점의 값이 될 수 있습니다. 읽는 짧은 구간에서 인터럽트를 잠시 막거나, 복사 전략을 사용해야 하는 이유입니다.

  • ISR에서 할 일: 불리언 플래그 설정, 짧은 카운터 증가, 현재 타임스탬프 저장처럼 실행 시간이 예측 가능한 처리
  • 메인 루프로 넘길 일: I2C 센서 읽기, 부동소수점 계산, 디스플레이 갱신, 네트워크 전송, 파일 기록
  • 검토할 충돌: 서보, 소프트웨어 시리얼, PWM, 타이머 라이브러리가 같은 타이머나 인터럽트 벡터를 쓰는지 확인
  • 디버깅 포인트: ISR 호출 횟수, 최대 호출 빈도, 처리되지 않은 플래그의 누적 여부를 별도 카운터로 관찰

폴링도 delay()에 묶이면 장점을 잃습니다

폴링이 단순하다고 해서 delay(1000) 뒤에 센서를 하나씩 읽는 코드를 뜻하지는 않습니다. 긴 지연은 버튼과 펄스를 놓치게 하고 통신 재시도도 늦춥니다. millis() 또는 하드웨어 타이머의 현재값을 기준으로 각 작업의 실행 시점을 비교하는 비차단 폴링을 사용하면 센서마다 다른 주기를 유지하면서 메인 루프를 계속 순환시킬 수 있습니다.

  1. 각 작업에 마지막 실행 시각과 실행 주기를 따로 둡니다.
  2. 현재 시각과 마지막 실행 시각의 차이가 주기 이상일 때만 작업을 수행합니다.
  3. 센서 읽기 자체가 오래 걸리면 한 번에 끝내지 말고 요청과 결과 회수를 상태로 분리합니다.
  4. 루프 한 바퀴의 최대 시간을 측정해 가장 짧은 입력 펄스보다 충분히 짧은지 확인합니다.

센서 제어 코드는 하드웨어 설정과 소프트웨어 계층이 함께 움직입니다. 드라이버와 개발환경의 역할을 더 넓게 이해하려면 디바이스 드라이버 통합개발환경의 개요도 참고할 만합니다. 센서 데이터시트만 읽는 데서 멈추지 않고, 라이브러리가 핀과 주변장치를 어떻게 점유하는지 확인하는 습관이 중요합니다.

정확도와 전력 소비 대결은 사용 조건에 따라 승자가 바뀝니다

샘플링 주기가 중요하면 폴링의 시간 흔들림을 관리해야 합니다

온도나 조도처럼 천천히 변하는 값은 100ms 또는 1초 간격의 폴링만으로 충분한 경우가 많습니다. 다만 네트워크 전송이나 디스플레이 갱신이 메인 루프를 오래 점유하면 실제 샘플링 간격이 100ms, 143ms, 118ms처럼 흔들릴 수 있습니다. 단순 모니터링이라면 큰 문제가 아니지만, 일정한 시간 간격을 가정하는 필터나 제어 알고리즘에서는 오차가 누적될 수 있습니다.

그렇다고 센서 읽기 전체를 타이머 인터럽트에 넣을 필요는 없습니다. 타이머 ISR은 “샘플 시점이 되었다”는 플래그만 만들고, 메인 루프가 플래그를 발견하면 I2C 통신을 시작하는 혼합 구조가 실용적입니다. 실제 취득 시각을 함께 기록하면 샘플 간격이 조금 흔들려도 계산에서 보정할 수 있습니다. 시간 정확도는 인터럽트로 확보하고 무거운 처리는 폴링으로 실행하는 방식입니다.

기계식 버튼은 다른 문제를 만듭니다. 한 번 눌러도 접점이 수 밀리초 동안 여러 번 튀기 때문에 CHANGE 인터럽트를 그대로 사용하면 한 번의 조작이 수십 번으로 집계될 수 있습니다. 폴링에서는 5~20ms 간격으로 읽어 연속된 동일 상태를 확인하기 쉽습니다. 인터럽트를 써야 한다면 최초 에지만 기록하고 일정 시간이 지난 뒤 메인 루프에서 상태를 재확인하는 디바운싱이 필요합니다.

  • 환경 센서: 100ms~수 초 주기의 폴링이 구현과 필터링에 유리합니다.
  • 제어 루프: 일정 주기의 타이머 플래그와 실제 측정 시각 기록을 결합합니다.
  • 기계식 입력: 인터럽트 횟수를 곧바로 버튼 횟수로 해석하지 말고 디바운싱을 적용합니다.
  • 펄스 계수: 최대 주파수에서 ISR 실행 시간이 주기의 충분히 작은 비율인지 확인합니다.
  • I2C 데이터 준비 핀: 인터럽트는 준비 상태만 알리고 레지스터 접근은 메인 루프에서 수행합니다.

배터리 기기에서는 인터럽트가 잠을 깨우는 열쇠가 됩니다

전력 소비 대결에서는 상황이 달라집니다. MCU가 계속 실행되며 핀을 확인하는 폴링은 저전력 사물인터넷 노드에서 배터리를 빠르게 소모할 수 있습니다. 이때 인터럽트는 평소 MCU를 슬립 모드에 두고, 동작 감지나 외부 스위치가 발생했을 때만 깨우는 수단이 됩니다. 이벤트가 드물수록 상시 폴링과의 소비 전력 차이가 커집니다.

다만 온도 센서를 10분마다 읽는 장치라면 외부 핀 인터럽트보다 저전력 타이머나 워치독으로 주기적으로 깨우는 방식이 더 자연스럽습니다. 반대로 문 열림 센서처럼 발생 시간을 예측할 수 없는 입력은 핀 변경 인터럽트가 적합합니다. 인터럽트와 폴링 중 하나만 고집하기보다 슬립 중에는 인터럽트, 깨어난 뒤에는 비차단 폴링으로 역할을 나누는 편이 좋습니다.

  1. 대기 시간, 측정 시간, 전송 시간을 각각 분리해 평균 전류를 계산합니다.
  2. 슬립에서 깨울 수 있는 핀이 보드마다 다르므로 MCU 데이터시트를 확인합니다.
  3. 센서 모듈의 전원 LED와 레귤레이터가 슬립 전류를 압도하지 않는지 측정합니다.
  4. 플로팅 입력이 반복해서 깨우지 않도록 풀업·풀다운 저항과 유효 에지를 정합니다.
  5. 깨어난 직후 센서 안정화 시간이 필요한지 데이터시트에서 확인합니다.
설계 조언: 저전력 프로젝트에서는 코드 방식만 비교하면 답을 놓칩니다. MCU 슬립 전류뿐 아니라 센서 대기 전류, 풀업 저항 전류, 무선 송신 시간까지 합산해야 실제 배터리 수명이 보입니다.

온실 환기 노드 한 대에서 두 방식을 끝까지 붙여 봅니다

온습도는 폴링하고 유량 펄스는 인터럽트로 셉니다

작은 온실에 아두이노 기반 환기·급수 노드를 설치한다고 가정해 보겠습니다. I2C 온습도 센서는 2초마다 읽고, 토양 수분 센서는 5초마다 읽으며, 팬 상태는 500ms마다 확인합니다. 여기에 관수량을 재는 유량 센서가 최대 초당 80개의 짧은 펄스를 출력하고, 사용자가 누르는 정지 버튼도 연결됩니다. 모든 입력을 인터럽트로 만들면 코드는 복잡해지고, 전부 폴링하면 네트워크 전송 중 유량 펄스를 놓칠 수 있습니다.

먼저 온습도와 토양 수분은 비차단 폴링으로 배치합니다. 두 값은 수십 밀리초 늦게 읽혀도 제어 결과에 거의 영향이 없으며, I2C 읽기를 메인 루프에 두면 통신 오류와 재시도 횟수를 기록하기 쉽습니다. 팬 상태 역시 500ms 폴링이면 충분합니다. 정지 버튼은 안전 요구가 엄격하지 않은 소형 시제품이라면 10ms 폴링과 30ms 디바운싱으로 처리해도 사람이 느끼는 반응은 즉각적입니다.

유량 센서는 인터럽트가 이깁니다. 네트워크 모듈이 순간적으로 30ms를 사용한다면 그동안 두세 개의 펄스가 들어올 수 있기 때문입니다. ISR에서는 flowPulseCount만 증가시키고 끝냅니다. 메인 루프는 1초마다 잠깐 원자적으로 카운터를 복사한 뒤, 복사본을 유량과 누적 사용량으로 환산합니다. 카운터 초기화와 복사 순서를 명확히 해 ISR과 메인 코드가 값을 동시에 바꾸는 문제도 막습니다.

  • 2초 작업: I2C 온습도 센서를 읽고 통신 실패 시 오류 카운터만 증가시킵니다.
  • 5초 작업: 토양 수분 값을 여러 번 샘플링해 중앙값을 구하고 급수 조건을 평가합니다.
  • 500ms 작업: 팬 피드백을 확인하되 일시적인 흔들림은 연속 실패 횟수로 거릅니다.
  • 10ms 작업: 정지 버튼을 폴링하고 3회 연속 동일할 때 상태 변경을 인정합니다.
  • 인터럽트 작업: 유량 펄스 카운터만 증가시키며 계산과 로그 출력은 하지 않습니다.
  • 1초 작업: 펄스 카운터를 안전하게 복사해 순간 유량과 누적 관수량을 계산합니다.

실제 동작 순서에서 실패 상황까지 확인합니다

오전 9시, 토양 수분 값이 설정 기준 아래로 내려가면 메인 루프가 급수 밸브를 엽니다. 물이 흐르기 시작하면서 유량 센서의 펄스가 인터럽트 카운터에 쌓입니다. 그 사이 2초 주기의 온습도 측정과 500ms 주기의 팬 확인은 각자의 시점에 실행됩니다. I2C 센서 응답이 한 번 실패해도 유량 ISR은 독립적으로 펄스를 세므로 관수량 기록은 끊기지 않습니다.

5초 뒤 시스템은 계산된 유량이 0인지 확인합니다. 밸브는 열렸는데 펄스가 없다면 물 부족, 막힘, 센서 배선 단선 중 하나로 판단해 밸브를 닫고 오류 상태를 남깁니다. 반대로 펄스가 비정상적으로 많다면 접점 잡음이나 잘못된 풀업, 입력 임계값 문제를 의심합니다. 이때 ISR 안에서 경보를 울리는 대신 메인 루프의 상태 머신이 밸브 차단, 경고 LED, 통신 보고를 순서대로 수행합니다.

사용자가 정지 버튼을 누르면 10ms 폴링이 입력을 발견하고 세 번의 연속 확인 뒤 밸브를 닫습니다. 약 30ms의 확정 시간이 들지만 일반적인 수동 조작에는 충분히 빠르며, 접점 바운스로 밸브가 여러 번 켜지고 꺼지는 현상을 막습니다. 만약 이 버튼이 사람을 보호하는 비상정지 장치라면 이야기가 달라집니다. 소프트웨어 폴링이나 일반 인터럽트만 믿지 않고 전원을 직접 차단하는 하드웨어 안전회로가 필요합니다.

  1. 시리얼 로그에 루프 최대 실행 시간, 초당 유량 펄스, I2C 오류 횟수를 함께 출력합니다.
  2. 펌프를 끈 상태에서 입력선을 흔들어 허위 펄스가 증가하는지 확인합니다.
  3. 무선 전송을 강제로 반복해도 계산된 유량이 기준 계측값과 일치하는지 비교합니다.
  4. I2C 센서 커넥터를 잠시 분리해 통신 실패가 전체 루프를 멈추지 않는지 시험합니다.
  5. 정지 버튼을 짧게 누르거나 길게 눌러 디바운싱과 상태 전환이 의도대로 작동하는지 검증합니다.

이 사례에서 폴링은 느린 센서를 단순하고 관찰 가능한 흐름으로 유지했고, 인터럽트는 짧은 유량 펄스를 보존했습니다. 승자는 하나가 아닙니다. 사건의 지속 시간이 루프 최악 실행 시간보다 짧은 곳에만 인터럽트를 배치하고, 나머지는 시간 기반 폴링으로 운영하는 것이 이 노드의 안정성을 만든 선택입니다. 다음 센서를 추가할 때도 먼저 최소 펄스 폭과 허용 지연을 적으면, 인터럽트 핀이 부족해지기 전에 필요한 자리와 필요 없는 자리를 구분할 수 있습니다.

아두이노 센서 읽기에 인터럽트가 항상 필요하지 않은 이유

댓글목록

등록된 댓글이 없습니다.