아두이노의 모든 디지털/아날로그 핀(A6, A7 제외)에서 RC 수신기 PWM 신호(또는 일반 PWM)를 읽고, 송신기 신호 손실 시 안전한 페일세이프 값을 출력하는 범용 코드입니다. 핀 변경 인터럽트(Pin Change Interrupt)를 활용해 pulseIn()처럼 블로킹하지 않고 효율적으로 측정합니다.
아래 영상은 이 코드(PWMread_RCfailsafe.ino)로 아두이노 우노를 서보 믹서로 사용하는 예시입니다.
페일세이브: 수신기 신호 끊김 시 엘리베이터 풀업, 에일러론 중립
파란 서보 방향: 스로틀 위치로 제어
파란 서보 이동 범위(레이트): 송신기 사이드 슬라이더로 조절
믹싱 온/오프: 기어 스위치로 전환
목차
서보/ESC의 PWM 제어 원리
RC 모델/로봇에서 아두이노 활용 예
코드 개요: RC 수신기 PWM 디코딩 + 페일세이프
PWMread_RCfailsafe.ino 사용법
수신기 프레임 레이트/주파수 표시
서보 믹싱 예제
접근 방식의 이유
한계점
서보와 속도 컨트롤러는 어떻게 PWM으로 제어되나?
서보/ESC 제어용 PWM(1000~2000μs 펄스 폭, 약 50Hz)에 대한 이해가 필요합니다. 좋은 설명 영상 참고. 또한 아두이노 IDE, 변수 타입, 제어문, 배열, Servo 라이브러리 기초 지식이 필요합니다.
RC 모델/로봇에서 아두이노 활용 예
서보 믹싱, 라이트/펌프/밸브 제어, 맞춤 시퀀스
비행 안정화/오토파일럿, 고도/방위 유지, 자동 레벨링, 회피/귀환 등 컨트롤러 구현
신호 손실/저전압 대응
모델 메모리 없이 동일 송신기로 여러 기체 제어
코드 개요: RC 수신기 PWM 디코딩 with 페일세이프
핀 변경 인터럽트로 PWM 측정. 선택한 핀들의 포트 레지스터(Port B/C/D)에 따라 3개 ISR(PCINT0_vect, PCINT1_vect, PCINT2_vect) 중 하나가 트리거됩니다. ISR 내부에서 어떤 핀이 변했는지 판별, micros()로 시간 기록, 펄스 폭과 주기 계산, 플래그 설정. 메인 루프에서 플래그 확인 후 데이터 처리.
주의: SoftwareSerial 등 핀 변경 인터럽트 쓰는 라이브러리와 충돌함.
핵심 함수 RC_decode(): 펄스 폭(1000~2000μs)을 ±100%(±1.0) 제어 신호로 변환. 페일세이프는 10~330Hz, 500~2500μs 범위 밖이면 사전 설정된 페일세이프 값 반환. 송신기별 캘리브레이션 값(RC_min[], RC_mid[], RC_max[])과 페일세이프 위치(RC_failsafe[])는 코드 내 배열로 설정.
예제 스케치 RC_Read_Example 다운로드 후 메인 스케치로 사용. .ino 파일을 같은 폴더에 넣으면 IDE에서 두 번째 탭으로 나타남.
3단계: 입력 핀 지정
PWMread_RCfailsafe 탭 → USER DEFINED VARIABLES 섹션 → pwmPIN[] 배열에 입력 핀 번호 나열. 순서가 채널 순서(첫 번째=채널 1). 핀 개수/순서 자유, A6/A7 불가. 아두이노 메가 미지원(수정 가능).
4단계: 사용 가능 함수 확인
5단계: 원시 펄스 폭 데이터 시리얼 출력 (캘리브레이션용)
RC_avail()로 새 데이터 확인 → print_RCpwm()로 시리얼 출력.
6단계: 송신기 캘리브레이션
시리얼 모니터의 원시 펄스 폭 값을 보고 RC_min[], RC_mid[], RC_max[] 배열 수정. 풀 레이트로 조작해 최대 해상도 확보.
7단계: 캘리브레이션된 채널 출력 확인
print_RCpwm() 주석 처리 → RC_decode(channel)로 ±1 범위 변환 → decimal2percentage()로 % 출력.
8단계: 페일세이프 위치 설정
RC_failsafe[] 배열에 각 채널별 페일세이프 값(±1 범위) 입력. 송신기 온/오프로 동작 확인. 수신기 자체 페일세이프 기능은 끄세요 (아두이노가 신호 손실 감지 못 함).
수신기 프레임 레이트 및 주파수 표시
PWM_read(채널)로 새 펄스 감지 → PWM_period(), PWM_freq()로 주기/주파수 추출. 채널 1이 프레임 첫 펄스이므로 권장. PWM_read()를 RC_decode()보다 먼저 호출해야 함 (같은 플래그 공유). 예제: RC_FrameRate.ino.
수신기 주기(보통 20ms) 알면 다음 데이터 도착 전까지 처리 시간 계산 가능. RC_avail()가 일정 시간(예: 21ms) 내 새 데이터 못 받으면 강제로 RC_decode() 호출해 페일세이프 트리거하거나 PID 루프를 일정 주기로 돌릴 수 있음.
now = millis();
if(RC_avail() || now - rc_update > 21) {
rc_update = now;
// RC_decode()로 입력 업데이트
// PID 컨트롤러 실행
// 서보 믹싱 적용
// 서보 위치 지정
}
서보 믹싱 예제
RC_ServoMixer_Example.ino 참조. 2채널(엘리베이터 CH2, 에일러론 CH3)을 믹싱해 2개 서보(D9, D10)로 출력. 서보 라이브러리 사용. 방향/레이트/서브트림 설정 가능.
믹스 공식: mix1 = CH2 - CH3, mix2 = CH2 + CH3 (출력 범위 -1~+1 클램프). 서보 펄스 폭(μs) 변환 함수 calc_uS()로 방향/레이트/서브트림 반영.
중립 1500μs, 최대 편차 ±500μs 기준: pulse = 1500 + (서보_위치% × 레이트 × 방향 + 서브트림) × 500. 방향/레이트/서브트림은 정적이거나 다른 채널 입력으로 동적 변경 가능.
이 접근 방식의 이유
pulseIn()은 펄스 대기 중 코드 블로킹, 다중 입력 시 데이터 손실. 핀 변경 인터럽트 + 직접 포트 조작으로 메인 루프 지연 최소화. 하지만 구현 복잡함. → 범용 .ino 파일로 모듈화해 프로젝트 간 이식 쉽게 만들었음. 복사→핀 지정→함수 호출만 하면 됨.