Все статьиHardware

Радиопередача на NRF24l01 -- радиосвязь между двумя Ардуинами

БА
Бексултан Айтен
CTO, Alashed
28 июня 2025 г.
9 мин чтения
Радиопередача на NRF24l01 -- радиосвязь между двумя Ардуинами

Опыт создания радиоаппаратуры на программируемых микроконтроллерах и радиомодулях NRF24l01. Схемы подключения, скетчи передатчика и приёмника, управление сервоприводом по радио.

*двумя Ардуинами. Ардуина -- это имя нарицательное, так называется любая плата с программируемым микроконтроллером в кругу ардуинщиков. Здесь имеются ввиду платы Arduino Uno/Nano и ESP32 Dev Module.

Всем привет! В этой статье поделюсь своим опытом по созданию радиоаппаратуры на программируемых микроконтроллерах и радиомодулях NRF24l01. Так что приятного чтения =)

Картинка 1 -- цель создания радиоаппаратуры
Картинка 1 -- цель создания радиоаппаратуры

Цель создания радиоуправления

Радиоаппаратуру я захотел сделать для самодельного радиоуправляемого самолета (картинка 1), а самому делать, потому что в заводских аппаратурах особо не настраивается как мне нужно и стоят аппаратуры дорого. А самодельную можно настроить на что угодно и стоит дешевле, но нужно потратить время.

Конечно, заводские аппаратуры красивее и компактнее, но мне все равно =)

Скачайте необходимую библиотеку для радиомодулей с GitHub автора. А также библиотека NRF24, если первой будет недостаточно.

Покупка первых радиомодулей и первое подключение

Картинка 2. Самодельное колхозное подключение проводов папа-папа к пинам радиомодулей
Картинка 2. Самодельное колхозное подключение проводов папа-папа к пинам радиомодулей

Первый шаг в создании радиоаппаратуры -- покупка и настройка радиомодулей. Я выбрал очень популярные и прикольные радиомодули NRF24l01, самую простую версию без усилителей. Ещё я купил Arduino Nano, которая должна была быть приёмником, но что-то она сломалась и не хочет работать. Поэтому пришлось её заменить на плату ESP32 Dev Module, обзор которой доступен по этой ссылке.

Эти радиомодули имеют такое свойство, что у них все контакты собраны в группу 2*4, и в макетную плату такая группа не вставляется. Значит, нужны провода с гнездом "мама". У меня таких не хватало на оба модуля, хватало только на один, поэтому для второго надо было наколхозить что-нибудь, либо ждать с AliExpress новые провода.

Сначала я решил наколхозить, результат колхоза смотрите на картинке 2. У меня оставались пины "мама" от платы Wemos D1 Mini. Железочки из пинов я вытащил, установил пластиковые корпуса на пины радиомодуля и воткнул с обратной стороны провода. Вроде крепко держались, но все равно не работало.

ФАТАЛЬНАЯ ОШИБКА -- как я испортил радиомодуль (или два)

Картинка 3. Один из двух моих первых радиомодулей
Картинка 3. Один из двух моих первых радиомодулей

Короче, вроде как-то приколхозил провода. Начал подключать к ардуинкам. Прочитал в интернете, что ардуиновских 3.3В радиомодулям не хватит. Нашел понижайку LM2596S, настроил её на 3.3В на выходе, четко выдает 3.3В. Все подключил по схеме из интернета, запускаю. Обе ардуинки пишут в порт что, мол, "Модуль не найден". Я начал смотреть в интернете, что же может быть. Везде говорили что неправильное подключение значит. Проверил подключение -- оказывается я у одного из модулей (или обоих, не помню) перепутал плюс и минус =) Ну, блин, молодец....

От неправильной полярности они даже нагрелись, довольно ощутимо. Так что повнимательнее там в подключении =)

На этом история этих двух малышей закончилась...

Картинка 4
Картинка 4

Новые радиомодули

Пошел на AliExpress купил новые радиомодули, уже с усилителями, и заодно комплект проводов 40 штук папа-мама. Прошло 2-3 недели, забрал, потом ещё сколько-то времени искал схемы, скетчи, потом ещё что-то. Наконец, недавно дошли руки и наконец всё собрал и заработало.

Итак, знакомьтесь -- новые радиомодули NRF24l01+PA/LNA (картинка 4), дальность связи до 1 км на открытой местности (в реальности около 1,5-2 км на открытой местности).

Стоят эти радиомодули с антеннами не сильно дороже, около 150-200 рублей за штуку. Так что сразу берем их =) Если питания хватит =))))

Ниже будут представлены схемы подключения и скетчи. Так что обязательно дочитайте до конца!

Теперь перейдём к распиновке и схеме подключения.

Картинка 5 -- распиновка радиомодуля NRF24l01
Картинка 5 -- распиновка радиомодуля NRF24l01

Схемы подключения

На картинке 5 представлена распиновка радиомодуля NRF24l01. Распиновка на разных версиях этих модулей одинаковая -- как без усилителя, так и с усилителем. Для стабильной работы модулей желательно обеспечить стабильный ток 400-500 мА 3.3В.

Радиомодуль использует для связи с микроконтроллером интерфейс SPI (от него пины CSN, MOSI, MISO, SCK). Пин IRQ не подключается. Пин CE это управляющий пин, на который подаётся сигнал для включения (подаётся 1) и выключения (подаётся 0) радиомодуля. Вернее -- не выключения, а отправки в режим ожидания с самым низким энергопотреблением.

Если вы будете запитывать схему с радиомодулями от батарей/аккумуляторов, то перед каждой отправкой сигнала нужно включать модуль, а после отправки выключать. Для этого в библиотеке предусмотрены специальные функции.

Если вы делаете летающую или ездящую модель с постоянным управлением, то необязательно выключать передатчик, либо можно сделать кэширование информации на приёмнике, и выключать передатчик при бездействии.

ВНИМАНИЕ! Крайне рекомендуется добавить в схему 2 электролитических конденсатора (обычные цилиндрические, черные/синие) на 10 мкФ (напряжение не важно, важна емкость), максимально близко к контактам радиомодулей, по одному на каждый модуль, на пины плюса питания и земли, во избежание скачков напряжения питания. Там, где белая полоска на корпусе конденсатора, там минус, подключаем к земле радиомодуля.

Подключаются радиомодули по такой схеме:

Пин NRF24l01Arduino Uno/Nano (передатчик TX)ESP32 (приёмник RX)
VCCЧерез понижайку до 3.3V подключаем на пин 5VЗапитать можно от встроенного преобразователя на 3.3В, у меня так работает.
GNDGNDGND
CSND8GPIO12
CED7GPIO14
SCKD13GPIO26
MOSID11GPIO27
MISOD12GPIO25
IRQНе подключаемНе подключаем

Аналоговый джойстик подключается по классике -- питание 5 вольт, земля, ось X на аналоговый вход А0, ось Y на аналоговый вход А1, пин кнопки на цифровой пин Arduino D2.

ВИДЕО ДЕМОНСТРАЦИЯ РАБОТЫ РАДИОАППАРАТУРЫ на Arduino + серво

С момента написания статьи я доработал схему, добавил сервопривод к ESP32 и доработал код. Доработанный код также есть внизу. Вот видео демонстрация управления серво по радио:

**Перейдите на Rutube по этой ссылке**, так как встроить видео Shorts с Rutube невозможно. А также смотрите на Rutube видео про радиоуправляемую машинку.

Скетчи и инструкция по загрузке

ЧТО ДЕЛАЮТ ЭТИ СКЕТЧИ?

В будущем можно доработать скетч для масштабирования показаний с джойстика и вывода на сервы/моторы, подключить к приёмнику сервопривод, драйвер с моторами и сделать полноценную радиоуправляемую модель! Советую начинать с наземных моделей -- машин. В случае неисправности машинка просто остановится, или, на худой конец, продолжит ехать, упрётся во что-нибудь и всё. А летающая модель упадёт и разобьётся.

Используйте простенькие корпуса для тестов, чтобы не жалко было поцарапать/сломать, например, сделайте корпус из картона.

Видео демонстрацию работы этих скетчей можете [посмотреть в моём телеграм канале по этой ссылке](https://t.me/arduino_uno_ws/333).

Передатчик: принимает значения с осей джойстика X и Y, а также кнопки джойстика. Собирает в массив из 3 элементов, передаёт массив по радио на канале, размер сообщения -- 6 байт (2Б ось X, 2Б ось Y, 1Б кнопка, 1Б контрольная сумма). Также во избежание получения случайных или искаженных сигналов передаётся контрольная сумма. Адрес "трубы" общения радиомодулей -- 1. Мощность -- низкая. Скорость -- 250 кБит в секунду. Скорость монитора порта -- 9600 бод.

Если радиомодуль не подключен или не исправен, либо плохой контакт в соединениях, в монитор порта выйдет сообщение "NRF24L01 не найден!".

Приёмник: получает сигнал по радио, декодирует в массив из 3 чисел, выводит их в монитор порта. Адрес, мощность, скорость -- такие же. Скорость порта 115200 бод для ESP32 и так же 9600 для Arduino. Пауза в получении и выводе информации в порт -- 50 миллисекунд (0.05 секунды, для стабильности). Если радиомодуль не подключен или не исправен, либо плохой контакт в соединениях, в монитор порта выйдет сообщение "NRF24L01 не найден!".

На случай, если вы будете использовать вместо ESP32 плату Arduino Uno/Nano, я доработал скетч приёмника под Arduino Uno/Nano и добавил после основных двух скетчей.

![Видео демонстрацию работы радиопередачи можно посмотреть в моём телеграм канале (нажми на эту картинку, чтобы перейти в телеграм)](https://t.me/arduino_uno_ws/333)

Скетч для Arduino Uno/Nano (передатчик TX)

```cpp

#include <SPI.h> // Библиотека для интерфейса связи платы с радиомодулем

#include <nRF24L01.h> // Основная библиотека для радиомодуля

#include <RF24.h> // Дополнительная библиотека для радиомодуля

// Пины для NRF24L01. Остальные пины указывать в данном случае не нужно, они стандартные для SPI (11, 12, 13)

#define CE_PIN 7

#define CSN_PIN 8

// Пины джойстика

#define JOYSTICK_X_PIN A0

#define JOYSTICK_Y_PIN A1

#define JOYSTICK_BUTTON_PIN 2

// Создаем объект радио

RF24 radio(CE_PIN, CSN_PIN);

// Адрес трубы

const byte address[6] = "00001";

// Буфер для передачи данных (массив байтов)

byte dataBuffer[6]; // 2 байта X + 2 байта Y + 1 байт кнопка + 1 байт контрольная сумма

void setup() {

// Старт монитора порта для отладки

Serial.begin(9600);

// Инициализация пинов джойстика

pinMode(JOYSTICK_X_PIN, INPUT);

pinMode(JOYSTICK_Y_PIN, INPUT);

pinMode(JOYSTICK_BUTTON_PIN, INPUT_PULLUP);

// Инициализация NRF24L01

if (!radio.begin()) {

Serial.println("NRF24L01 не найден!");

while (1);

}

// Создаём новую "трубу" для связи, адрес 00001

// Доступные номера "труб" - от 00001 до 00005.

// Номер 00000 занят, это специальный канал для приёма ACK-пакетов.

radio.openWritingPipe(address);

// Мощность ставим низкую,

// чтобы слишком много не потребляли модули,

// и для теста на столе этого достаточно

radio.setPALevel(RF24_PA_LOW);

// Скорость ставим 250 килобит в секунду, этого тоже достаточно для теста.

// Доступные варианты:

// 1. RF24_250KBPS - низкая скорость, высокая стабильность, дальность и качество сигнала.

// 2. RF24_1MBPS - средняя скорость, в 4 раза больше, стабильность и дальность ниже, но всё ещё нормально.

// 3. RF24_2MBPS - высокая скорость, в 8 раз больше первой, но не стабильная и сигнал может потеряться. Дальность маленькая.

radio.setDataRate(RF24_250KBPS);

// Отключаем режим приёмника, так как мы передатчик

radio.stopListening();

// Отчитаемся, что мы готовы передавать данные, всё настроено

Serial.println("Arduino Uno - Пульт готов!");

}

void loop() {

// Чтение данных с джойстика

int xValue = analogRead(JOYSTICK_X_PIN);

int yValue = analogRead(JOYSTICK_Y_PIN);

bool buttonState = !digitalRead(JOYSTICK_BUTTON_PIN);

// Упаковываем данные в массив байтов

dataBuffer[0] = highByte(xValue); // Старший байт X

dataBuffer[1] = lowByte(xValue); // Младший байт X

dataBuffer[2] = highByte(yValue); // Старший байт Y

dataBuffer[3] = lowByte(yValue); // Младший байт Y

dataBuffer[4] = buttonState; // Состояние кнопки

dataBuffer[5] = dataBuffer[0] ^ dataBuffer[1] ^ dataBuffer[2] ^ dataBuffer[3] ^ dataBuffer[4]; // Контрольная сумма, состоит из связки всех показаний

// Отчёт об отправке в Serial

Serial.print("Отправка - X: ");

Serial.print(xValue);

Serial.print(" Y: ");

Serial.print(yValue);

Serial.print(" Button: ");

Serial.println(buttonState);

// Отправка данных

bool success = radio.write(dataBuffer, sizeof(dataBuffer));

// Если данные успешно отправлены, похвастаемся =)

if (success) {

Serial.println("Данные отправлены успешно!");

} else { // Если не отправлены, сообщим:

Serial.println("Ошибка отправки!");

}

delay(100); // Пауза

}

`

Скетч для ESP32 (приёмник RX)

```cpp

#include <SPI.h>

#include <nRF24L01.h>

#include <RF24.h>

// Пины для NRF24L01 на ESP32

#define CE_PIN 12

#define CSN_PIN 14

#define SCK_PIN 26

#define MISO_PIN 25

#define MOSI_PIN 27

// Создаем объект радио

RF24 radio(CE_PIN, CSN_PIN);

// Адрес трубы

const byte address[6] = "00001";

// Буфер для приема данных

byte dataBuffer[6];

// Настройка SPI с пользовательскими пинами

SPIClass spi = SPIClass(HSPI);

void setup() {

Serial.begin(115200);

// Инициализация SPI с пользовательскими пинами

spi.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CSN_PIN);

// Инициализация NRF24L01

if (!radio.begin(&spi)) {

Serial.println("NRF24L01 не найден!");

while (1);

}

radio.openReadingPipe(0, address);

radio.setPALevel(RF24_PA_LOW);

radio.setDataRate(RF24_250KBPS);

radio.startListening();

Serial.println("ESP32 - Приемник готов!");

Serial.println("Ожидание данных...");

}

void loop() {

if (radio.available()) {

// Чтение данных

radio.read(dataBuffer, sizeof(dataBuffer));

// Проверка контрольной суммы

byte checksum = dataBuffer[0] ^ dataBuffer[1] ^ dataBuffer[2] ^ dataBuffer[3] ^ dataBuffer[4];

if (checksum == dataBuffer[5]) {

// Распаковываем данные

int xValue = (dataBuffer[0] << 8) | dataBuffer[1];

int yValue = (dataBuffer[2] << 8) | dataBuffer[3];

bool buttonState = dataBuffer[4];

// Вывод в монитор порта

Serial.print("Получено - X: ");

Serial.print(xValue);

Serial.print(" Y: ");

Serial.print(yValue);

Serial.print(" Button: ");

Serial.println(buttonState);

} else {

Serial.println("Ошибка контрольной суммы!");

}

}

delay(50);

}

`

Скетч для ESP32 с подключенным сервоприводом (приёмник RX)

```cpp

#include <SPI.h>

#include <nRF24L01.h>

#include <RF24.h>

#include <ESP32Servo.h>

// Пины для NRF24L01 на ESP32

#define CE_PIN 12

#define CSN_PIN 14

#define SCK_PIN 26

#define MISO_PIN 25

#define MOSI_PIN 27

// Пин для сервопривода

#define SERVO_PIN 13

// Создаем объекты

Servo myServo;

RF24 radio(CE_PIN, CSN_PIN);

// Адрес трубы

const byte address[6] = "00001";

// Буфер для приема данных

byte dataBuffer[6];

// Настройка SPI с пользовательскими пинами

SPIClass spi = SPIClass(HSPI);

// Переменные для калибровки джойстика

int joyMinX = 0; // Минимальное значение X с джойстика

int joyMaxX = 1023; // Максимальное значение X с джойстика

int deadZone = 50; // Мертвая зона в центре

void setup() {

Serial.begin(115200);

// Разрешаем использование всех таймеров

ESP32PWM::allocateTimer(0);

ESP32PWM::allocateTimer(1);

ESP32PWM::allocateTimer(2);

ESP32PWM::allocateTimer(3);

// Инициализация сервопривода

myServo.setPeriodHertz(50); // Стандартная частота сервопривода

myServo.attach(SERVO_PIN, 500, 2400); // Минимальный и максимальный импульс в микросекундах

myServo.write(90);

Serial.println("Сервопривод инициализирован с ESP32Servo");

// Инициализация SPI с пользовательскими пинами

spi.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CSN_PIN);

// Инициализация NRF24L01

if (!radio.begin(&spi)) {

Serial.println("NRF24L01 не найден!");

while (1);

}

radio.openReadingPipe(0, address);

radio.setPALevel(RF24_PA_LOW);

radio.setDataRate(RF24_250KBPS);

radio.startListening();

Serial.println("ESP32 - Приемник с сервоприводом готов!");

Serial.println("Ожидание данных...");

delay(1000);

}

void loop() {

if (radio.available()) {

// Чтение данных

radio.read(dataBuffer, sizeof(dataBuffer));

// Проверка контрольной суммы

byte checksum = dataBuffer[0] ^ dataBuffer[1] ^ dataBuffer[2] ^ dataBuffer[3] ^ dataBuffer[4];

if (checksum == dataBuffer[5]) {

// Распаковываем данные

int xValue = (dataBuffer[0] << 8) | dataBuffer[1];

int yValue = (dataBuffer[2] << 8) | dataBuffer[3];

bool buttonState = dataBuffer[4];

// Вывод в монитор порта

Serial.print("Получено - X: ");

Serial.print(xValue);

Serial.print(" Y: ");

Serial.print(yValue);

Serial.print(" Button: ");

Serial.print(buttonState);

// Управление сервоприводом по оси X

controlServo(xValue);

Serial.println();

} else {

Serial.println("Ошибка контрольной суммы!");

}

}

delay(50);

}

void controlServo(int joyX) {

// Применяем мертвую зону

if (abs(joyX - 512) < deadZone) {

// В мертвой зоне - серво в центре

myServo.write(90);

Serial.print(" | Серво: 90° (центр)");

return;

}

// Масштабируем значение джойстика в угол сервопривода (0-180°)

int servoAngle = map(joyX, joyMinX, joyMaxX, 0, 180);

// Ограничиваем значения

servoAngle = constrain(servoAngle, 0, 180);

// Устанавливаем угол сервопривода

myServo.write(servoAngle);

// Вывод информации о серво

Serial.print(" | Серво: ");

Serial.print(servoAngle);

Serial.print("°");

}

`

Скетч для Arduino Uno/Nano (приёмник RX)

```cpp

#include <SPI.h>

#include <nRF24L01.h>

#include <RF24.h>

// Пины для NRF24L01

#define CE_PIN 7

#define CSN_PIN 8

// Создаем объект радио

RF24 radio(CE_PIN, CSN_PIN);

// Адрес трубы

const byte address[6] = "00001";

// Буфер для приема данных

byte dataBuffer[6];

// Настройка SPI с пользовательскими пинами

SPIClass spi = SPIClass(HSPI);

void setup() {

Serial.begin(115200);

// Инициализация SPI с пользовательскими пинами

spi.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CSN_PIN);

// Инициализация NRF24L01

if (!radio.begin(&spi)) {

Serial.println("NRF24L01 не найден!");

while (1);

}

radio.openReadingPipe(0, address);

radio.setPALevel(RF24_PA_LOW);

radio.setDataRate(RF24_250KBPS);

radio.startListening();

Serial.println("ESP32 - Приемник готов!");

Serial.println("Ожидание данных...");

}

void loop() {

if (radio.available()) {

// Чтение данных

radio.read(dataBuffer, sizeof(dataBuffer));

// Проверка контрольной суммы

byte checksum = dataBuffer[0] ^ dataBuffer[1] ^ dataBuffer[2] ^ dataBuffer[3] ^ dataBuffer[4];

if (checksum == dataBuffer[5]) {

// Распаковываем данные

int xValue = (dataBuffer[0] << 8) | dataBuffer[1];

int yValue = (dataBuffer[2] << 8) | dataBuffer[3];

bool buttonState = dataBuffer[4];

// Вывод в монитор порта

Serial.print("Получено - X: ");

Serial.print(xValue);

Serial.print(" Y: ");

Serial.print(yValue);

Serial.print(" Button: ");

Serial.println(buttonState);

} else {

Serial.println("Ошибка контрольной суммы!");

}

}

delay(50);

}

`

Прошивка Arduino Uno/Nano: просто создайте новый скетч в Arduino IDE, вставьте код, приведённый выше, сохраните скетч, подключите плату к компьютеру, выберите плату в меню Arduino IDE, выберите порт, нажмите Загрузка. Дождитесь загрузки скетча, отключите плату и приступите к прошивке ESP32!

Прошивка ESP32: убедитесь, что в вашей среде Arduino IDE есть платы ESP32. Если их нет, прочитайте статью по добавлению плат в Arduino IDE (нажмите сюда). Если есть, ЗАЖМИТЕ КНОПКУ BOOT НА ПЛАТЕ, подключите плату к компьютеру, ОТПУСТИТЕ КНОПКУ BOOT (для ввода платы в режим прошивки), выберите плату ESP32 Dev Module, выберите порт. Создайте новый скетч, вставьте код сверху, сохраните, нажмите Загрузка. Если появится сообщение ошибки, что не удалось загрузить прошивку из-за истечения времени ожидания, снова нажмите Загрузка и нажимайте и отпускайте кнопку BOOT на плате, когда статус загрузки перейдёт в "Загрузка...".

Спасибо за внимание! Удачи в повторении проекта!

Попробуйте Alashed бесплатно

Подключите школу к пилоту. Генерируйте КМЖ за 2 минуты, ведите CodeStudio уроки, заказывайте оборудование — всё в одном месте.

Попробовать бесплатноДемо