|

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Картинка 4

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

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

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

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

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

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

Картинка 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 и доработал код. Доработанный код также есть внизу. Вот видео демонстрация управления серво по радио:

Смотреть на YouTube

Смотреть на Rutube

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

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

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

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

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

Видео демонстрацию работы этих скетчей можете посмотреть в моём телеграм канале по этой ссылке.

Передатчик: принимает значения с осей джойстика 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 и добавил после основных двух скетчей.

Видео демонстрацию работы радиопередачи можно посмотреть в моём телеграм канале (нажми на эту картинку, чтобы перейти в телеграм)
Скетч для Arduino Uno/Nano (передатчик TX)
#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)
#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)
#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(" | Сервo: 90° (центр)");
    return;
  }
  
  // Масштабируем значение джойстика в угол сервопривода (0-180°)
  int servoAngle = map(joyX, joyMinX, joyMaxX, 0, 180);
  
  // Ограничиваем значения
  servoAngle = constrain(servoAngle, 0, 180);
  
  // Устанавливаем угол сервопривода
  myServo.write(servoAngle);
  
  // Вывод информации о серво
  Serial.print(" | Сервo: ");
  Serial.print(servoAngle);
  Serial.print("°");
}

Скетч для Arduino Uno/Nano (приёмник RX)
#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 на плате, когда статус загрузки перейдёт в «Загрузка…».

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

(поставь 5 звёзд, чё ты =)))) спасибо! =)

4.9/5 - (17 голосов)


Поделись!
×

Пожалуйста, отключите блокировщик рекламы!
Реклама помогает автору создавать новые статьи!
Спасибо ❤️


Как отключить блокировщик?
Для этого нажмите на значок расширения блокировщика и нажмите "Выключить на этом сайте", затем обновите страницу. Спасибо!
×

Пожалуйста, отключите блокировщик рекламы!
Реклама помогает автору создавать новые статьи!
Спасибо ❤️