Эффективный читатель PWM и декодер RC-приёмника на Arduino с встроенным Fail-Safe
Компоненты и материалы
| Arduino UNO | × | 1 |
| SG90 Micro-servo motor | × | 2 |
О проекте
Проект содержит универсальный эффективный код для чтения RC-приёмника (или любого PWM-сигнала) на любом входном пине Arduino с применением fail-safe при потере сигнала передатчика.
Видео ниже демонстрирует Arduino Uno как миксер серв с кодом PWMread_RCfailsafe.ino (в разделе «Код»).
Функции автоматически настраивают регистры прерываний и легко переносятся между проектами с разными пинами.
В примере видео:
- Fail-safe активируется при потере сигнала: канал элеватора — полный вверх, элероны — нейтраль.
- Направление синего серво задаёт газ.
- Диапазон движения (rates) синего серво — ползунок на пульте.
- Микшер вкл/выкл — переключатель Gear на пульте.
Содержание
- Как сервоприводы управляются PWM
- Примеры использования Arduino в RC-моделях/роботах
- Обзор кода: декодирование PWM от RC-приёмника с fail-safe
- Как использовать PWMread_RCfailsafe.ino
- Отображение частоты и периода кадров приёмника
- Пример микширования серв
- Обоснование подхода
- Ограничения
Как сервоприводы и регуляторы скорости управляются PWM?
Предполагается понимание PWM-сигналов (1–2 мс импульс, период ~20 мс). Хорошее видео-объяснение здесь.
Также нужны базовые знания: Arduino IDE, переменные float/boolean/int, циклы if/for, массивы, библиотека Servo.
Примеры использования Arduino в RC/роботах
- Микширование серв, управление светом/насосами/клапанами, пользовательские последовательности…
- Создание контроллеров: стабилизация полёта/автопилот, удержание курса/высоты/глубины, автоуровень, избегание препятствий, возврат домой…
- Реакция на потерю сигнала или низкое напряжение батареи…
- Один пульт для нескольких моделей без перенастройки и model memory.
Обзор кода: декодирование PWM с fail-safe
Код измеряет PWM через прерывания по изменению пина (Pin Change Interrupts). Функции автоматизируют настройку прерываний и извлечение данных с любого цифрового/аналогового пина (кроме A6, A7) на Uno, Nano, Pro Mini. Удобно для новичков.
Цель — универсальный «модуль» RC-приёмника с fail-safe для быстрого переноса между проектами. Пример кода в разделе «Как использовать» — лишь демонстрация.
Важно: код несовместим с SoftwareSerial и другими библиотеками, использующими pin change interrupts.
Как работает (для заинтересованных):
- Входные пины задаются массивом — любая длина.
- Функция настройки включает pin change interrupts, выставляя регистры для каждого пина.
- Изменение напряжения на выбранных пинах вызывает один из трёх ISR в зависимости от порта:
ISR(PCINT0_vect)— Port B,ISR(PCINT1_vect)— Port C,ISR(PCINT2_vect)— Port D. - В ISR цикл FOR и IF определяют, какой пин изменился и к какому RC-каналу принадлежит. Фиксируется время через
micros()перед возвратом вloop(). - Интервалы между сменами пинов используются для расчёта ширины импульса и периода повторения.
- Флаги в ISR сигнализируют о получении новых импульсов.
- Флаги используются остальными функциями для извлечения и обработки данных.
На YouTube Joop Brokking (первые 8 мин) понятно объясняет этот метод для другого проекта.
Вся эта деталь скрыта в PWMread_RCfailsafe.ino (скачивается ниже).
Полезно о манипуляции портами: tronixstuff.com
Помимо прерываний, функция RC_decode() конвертирует ширину импульса (1000–2000 мкс) в ±100% сигнал управления. Fail-safe проверяет валидность сигнала по толерансам 10–330 Гц и 500–2500 мкс. При потере возвращает заданное fail-safe значение.
Калибровочные значения для конкретного пульта и fail-safe позиции настраиваются в PWMread_RCfailsafe.ino.
Как использовать PWMread_RCfailsafe.ino
Шаг 1: Пример железа с Arduino Uno
Если повторяете пример:
- Питание приёмника от 5V и GND Arduino.
- Сигнальные пины приёмника → пины 2–7 Arduino (female-male провода). Для 2-канального — только 2 и 3.
- Для примера микшера: серво 1 → пин 9, серво 2 → пин 10.

Шаг 2: Копируйте PWMread_RCfailsafe.ino в папку скетча
Пример скетча RC_Read_Example доступен для скачивания. Используйте его как основной.
Скопируйте файл PWMread_RCfailsafe.ino в папку с основным скетчем. При следующем открытии в IDE появится вторая вкладка с кодом.

Шаг 3: Укажите входные пины
Откройте основной скетч в IDE. Перейдите на вкладку PWMread_RCfailsafe, прокрутите к «USER DEFINED VARIABLES», введите пины в массив pwmPIN[].
Любое количество пинов, любой порядок. Чем больше входов, больше времени тратится на ISR. A6 и A7 — только аналоговые, нельзя использовать. Arduino MEGA пока не поддерживается (можно добавить при необходимости).
Первый элемент массива — канал 1, второй — канал 2 и т.д. Лучше соответствовать нумерации каналов приёмника.

Шаг 4: Изучите доступные функции

Шаг 5: Вывод сырых данных PWM в Serial
Загрузите RC_Read_Example, включите пульт, выведите сырые данные. Используйте RC_avail() для проверки новых данных на всех каналах, затем print_RCpwm() для отправки в Serial.

Шаг 6: Калибровка пульта
По данным из print_RCpwm() вручную скорректируйте массивы RC_min[], RC_mid[], RC_max[] для приведения каждого канала к диапазону ±100%.

Шаг 7: Вывод откалиброванных каналов
Закомментируйте print_RCpwm(). Используйте RC_decode(channel) для получения значения ±1. Выводите через decimal2percentage() и Serial.println().

Шаг 8: Установка fail-safe
Настройте позиции fail-safe в массиве RC_failsafe[] для каждого канала (диапазон ±1). Включите/выключите пульт — проверьте срабатывание.
Теперь RC-вход готов к использованию в скетче.
Возможно, нужно отключить fail-safe в самом приёмнике, иначе Arduino не сможет среагировать на потерю сигнала.

Отображение частоты и периода кадров приёмника
Период и частота кадров можно вывести в Serial. Проверьте новые данные на выбранном канале функцией PWM_read(номер канала), затем используйте PWM_period() и PWM_freq(). Пример в RC_FrameRate.ino.
Лучше использовать первый канал — он первый в кадре. PWM_read() использует те же флаги, что RC_decode(), поэтому вызывайте PWM_read() первым.

Период полезен: если RC_avail() не видит новых данных за заданное время (напр. 21 мс), запустите RC_decode() для триггера fail-safe или поддержания работы программы (ПИД-контроллер) на стабильной частоте.
Реализовано в RC_Read_Example.ino:
now = millis();
if(RC_avail() || now - rc_update > 21) {
rc_update = now;
// update RC input data using RC_decode()
// run a PID controller
// apply servo mixing
// position the servos
}
Пример микширования серв
Включён RC_ServoMixer_Example.ino — микширование двух каналов (2 и 3: элеватор и элерон). Показаны настройка направления, rates, sub-trim. Библиотека Servo управляет сервами на пинах 9 и 10.

Микширование: сложение/вычитание каналов, ограничение выхода ±1.
mix1 = channel 2 - channel 3 (elv - ail)
mix2 = channel 2 + channel 3 (elv + ail)
Перед подачей на серво конвертируем ±100% в импульс мкс. В примере используется функция calc_uS().

Формула: pulse, uS = 1500 + (servo_position_% * rates * direction + sub trim) * 500.
Направление, rates, sub trim могут быть статическими или динамически меняться по другому каналу пульта.
Обоснование подхода
Можно читать RC через pulseIn(PIN, HIGH), но она блокирует loop() в ожидании импульса — теряется процессорное время. При нескольких входах теряются данные.
Для скорости лучше pin change interrupts + прямая манипуляция портами — loop() работает с минимальной задержкой. Но это сложнее и дольше, чем pulseIn().
Поэтому написан универсальный код: копируете .ino файл в папку скетча, указываете пины, используете функции. Получаем преимущества обоих подходов.
Ограничения
Функция micros()
micros() считает шагами 4 мкс → точность 4 мкс при измерении 1000–2000 мкс. Практически достаточно. Можно улучшить до 0.5 мкс через таймерные прерывания: инструкция.
Эффективность PWMread_RCfailsafe.ino
При чтении 6–9 каналов 1.4–2.0% процессорного времени уходит на ISR — вполне приемлемо.
Время работы ISR в зависимости от числа каналов:
- 1 канал < 8 мкс
- 2 канала < 12 мкс
- 3 канала < 16 мкс
- 4 канала < 20 мкс
- 5 каналов < 20 мкс
- 6 каналов < 24 мкс
Чем больше каналов, дольше ISR (цикл FOR проходит все каналы при каждом вызове). При низких частотах (50 Гц) это незначительно.
Вход/выход из ISR ~4 мкс. На один импульс ISR срабатывает 2 раза (вверх/вниз).
Время измерения 1 импульса при 6 входах: 2 × (4 + 24) = 56 мкс (мин. измеряемая ширина).
Чтение всех 6 каналов: 336 мкс (6 × 56).
При периоде 20 мс прерывания активны 1.68% времени (0.000336/0.02) — намного лучше pulseIn() (блокирует до 20 мс на пин).
При 2 входах — всего 0.16%.
Максимальная практическая частота (Гц)
Для других целей макс. ~2.5 кГц (100 шагов разрешения micros()). При 1 входе на этой частоте 3% времени в прерывании → мин. скважность 0.03 → мин. импульс 12 мкс. Для выше частот перепишите ISR под задачу.
Код
- PWMread_RCfailsafe
- RC_Read_Example
- RC_FrameRate
- RC_ServoMixer_Example
PWMread_RCfailsafe Arduino
Содержит функции и ISR для декодирования RC-приёмника с fail-safe. Скопируйте в папку основного скетча (появится как вторая вкладка). Следуйте инструкциям в файле.
/* Kelvin Nelson 24/07/2019
* Pulse Width Modulation (PWM) decoding of RC Receiver with failsafe
* ... (полный код из оригинала) ...
*/
RC_Read_Example Arduino
Пример для калибровки и проверки fail-safe. Требует PWMread_RCfailsafe.ino в той же папке.
// ... (код из оригинала) ...
RC_FrameRate Arduino
Вывод частоты и периода кадров RC-приёмника. Требует PWMread_RCfailsafe.ino.
// ... (код из оригинала) ...
RC_ServoMixer_Example Arduino
Пример микширования двух каналов на две сервы. Требует PWMread_RCfailsafe.ino.
// ... (код из оригинала) ...
Схемы
RC-приёмник питается от 5V и GND ICSP, 6 сигнальных выходов → пины 2–7.
Микро-серво 1: 5V, GND, сигнал → пин 9.
Микро-серво 2: 3.3V, GND, сигнал → пин 10.
Производственный процесс
- Ввод-вывод в C: stdio.h, потоки stdin/stdout/stderr, функции getchar/putchar, gets/puts, scanf/printf
- Создание анимации и игр на LCD 16×2 с Arduino UNO
- Умный вольтметр на Arduino и смартфоне — простое DIY-проект
- Логгер температуры и влажности на Arduino с DHT11 и Ethernet Shield
- Чтение температуры и влажности DHT11 через Blynk — пошаговый урок для Arduino
- Управление Arduino Uno из Python 3: команды через последовательный порт и демонстрация с LED
- Клеточный автомат «Жизнь» Конвея на Arduino Nano с OLED 128x64
- Сборка компактного FM-радио на Arduino Nano и RDA5807M
- FM-радиоприемник на Arduino и модуле TEA5767
- Гальванически разделённый аналоговый вход для Arduino на LM331 и оптопаре 4N25