먼저 알아둘 용어
폴링(polling)·비차단 처리 — 폴링은 프로그램이 반복해서 입력 상태를 확인하는 방식입니다. 비차단 처리는 기다리는 동안 한 함수에 머무르지 않고 다른 작업으로 돌아오는 구조입니다.
unsigned·롤오버(rollover) — unsigned는 음수가 없는 정수 형식입니다. 롤오버는 카운터가 최대값 다음에 0으로 돌아가는 현상으로, 같은 폭의 unsigned 뺄셈을 이용해 짧은 경과 시간을 계산할 수 있습니다.
millis·ms — 예제의 millis()는 실행 환경이 제공하는 밀리초 시간값입니다. 1 ms는 0.001초이며, 시간값의 갱신 방식과 저전력 상태의 동작은 플랫폼마다 확인해야 합니다.

다음 실행 시각을 확인하고 곧바로 다른 작업으로 돌아옵니다
LED(Light-Emitting Diode)를 1초 간격으로 바꾸는 동안 버튼도 읽어야 한다면, 매번 1초를 기다리는 대신 현재 시각과 이전 실행 시각의 차이를 검사합니다. 아직 간격이 되지 않았으면 다른 짧은 일을 하고 루프(loop)를 반복합니다. 이 구조가 모든 입력을 반드시 포착하거나 하드웨어·인터럽트(interrupt)의 동작을 대신하는 것은 아닙니다.
다음은 작업 배치를 보여 주는 구조 예입니다. readInputsQuickly(), updateTimedOutput(), runOtherShortTasks()는 목적에 맞게 구현해야 합니다. 이 함수 안에서도 긴 대기가 없어야 합니다.
uint32_t previous = 0;
const uint32_t interval = 1000;
void loop() {
uint32_t now = (uint32_t)millis();
readInputsQuickly();
if ((uint32_t)(now - previous) >= interval) {
previous = now;
updateTimedOutput();
}
runOtherShortTasks();
}
차이를 unsigned 형식으로 계산하면 카운터가 최대값에서 0으로 넘어가는 경계를 다룰 수 있습니다. 예를 들어 이전 시각이 0xFFFFFFF0이고 현재가 0x00000020이면 32비트 unsigned 차이는 48 ms입니다. 실제 경과 시간을 알 수 없을 만큼 긴 기간 동안 검사를 중단하면 이 방법으로 전체 시간을 복원할 수 없습니다. 작은 간격을 충분히 자주 검사하는 용도이며, 시간값을 부호 있는 int로 줄여 저장하지 않습니다.
previous=now는 이번에 처리한 시점부터 다음 간격을 잡습니다. 정확한 주기 위상을 유지하려고 이전 기준 시각에 간격을 더하는 방식과는 다릅니다. 작업이 늦어진 뒤 밀린 실행을 몰아서 할지, 한 번만 처리하고 재개할지는 장치의 요구에 따라 정합니다. 밀린 일을 무제한 반복하면 다른 입력을 처리할 시간이 다시 부족해질 수 있습니다.
비차단 구조에도 확인할 것이 남습니다
센서 읽기, 문자열 수신, 통신 재시도 같은 함수 내부에서 오래 기다리면 delay를 지운 효과가 줄어듭니다. 루프의 가장 긴 실행 시간과 실제 입력 펄스 폭을 비교합니다. 한 번의 루프 실행 시간보다 짧은 입력 펄스는 폴링만으로 놓칠 수 있습니다. 주기 작업이 제때 실행되는지와 짧은 입력을 빠짐없이 감지하는지는 각각 시험해야 합니다.