Ардуино: радиомодуль nRF24L01

На прошлом уроке мы научились дистанционно передавать сигнал от одного устройства к другому с помощью радиомодуля с частотой 433 МГц. Этот модуль вполне пригоден для управления одним роботом или другим устройством, где требуется передача сигналов только в одном направлении и с небольшой скоростью (до 4 кб/с).

Если мы хотим передавать большие массивы данных в обоих направлениях, да еще и одновременно между несколькими устройствами, нам потребуется гораздо более «умное» устройство. Подходящим в данном случае можно назвать популярный радиомодуль nRF24L01, который мы и изучим на данном уроке.

Радиомодуль nRF24L01

Модуль nRF24L01 — это цифровой приемник и передатчик, заключенный в одной маленькой микросхеме. Размер платы, на которой размещается микросхема, необходимая обвязка и небольшая антенна составляет всего 15 x 29 мм. Краткая спецификация радиомодуля:

  • несущая частота: 2.4 ГГц;
  • рабочее напряжение: от 3.3 до 3.6 В;
  • дальность: до 100 м на открытом пространстве, и до 30 м в помещении;
  • скорость: <2 Мб/c (250kbps, 1Mbps и 2Mbps);
  • максимальная выходная мощность: +20 дБм;
  • коэффициент усиления антенны (пиковая): 2 dBi;
  • количество каналов: 125.

Для чего можно использовать этот радиомодуль? К примеру, для управления мобильным роботом: двигаем джойстик на пульте — робот едет в нужном направлении. В обратную сторону этот же робот может отправлять данные телеметрии: показания одометра, инклинометра, и разных других датчиков.

Другой вариант применения — сбор показаний дистанционных датчиков. nRF24L01 умеет работать с топологией один-ко-многим. Это значит, что один радиомодуль может держать связь одновременно с множеством других. Благодаря этому,  датчики температуры, влажности и освещенности, установленные по всей площади большого цеха или теплицы могут передавать показания на базовую станцию для последующего анализа.

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

Займемся подключением nRF24L01 к Ардуино Уно. Наша задача — соединить радиоканалом два контроллера и осуществить передачу каких-нибудь данных между ними.

1. Подключение

Радиомодуль nRF24L01 подключается к Ардуино при помощи SPI интерфейса. В зависимости от используемой библиотеки может использоваться дополнительный выход прерывания IRQ. В нашем уроке мы обойдемся без него. Схема подключения будет выглядеть следующим образом:

Радиомодуль nRF24L01 GND VCC CE CSN MOSI MISO SCK
Ардуино Уно GND +3.3V 7 8 11 12 13

Важно отметить, что питается модуль напряжением 3.3 Вольта. На платах многих Ардуино есть встроенный стабилизатор напряжения на 3.3 В, который, однако, не обладает достаточной мощностью для правильной работы nRF24L01. Особенно эта проблема актуальна на Ардуино Мега 256. В связи с этим, для питания радиомодулей nRF24L01 рекомендуется использовать внешние стабилизаторы.

Принципиальная схема

Схема подключения nRF24L01 к Ардуино Уно

Внешний вид макета

Схема подключения nRF24L01 к Ардуино Уно

Проверим еще раз все контакты. Главное, не путаем напряжение: стандартные для Ардуино 5 Вольт могут повредить модуль.

2. Программа для передатчика

Для организации передачи данных будем использовать сразу две библиотеки. Первая — RF24, необходима для работы с модулем nRF24L01 по интерфейсу SPI. Вторая — SerialFlow, служит для пакетной передачи данных.

Для проверки работоспособности радиотракта будем передавать текущее значение таймера, с одного модуля на другой.

#include <SerialFlow.h>

SerialFlow rd(7,8);

const unsigned long data_to = 100; 
unsigned long tm, data_next;

void setup(void){
    rd.setPacketFormat(2, 1);
    rd.begin(0xF0F0F0F0E1LL,0xF0F0F0F0D2LL);
}

void loop(void){
    tm = millis();
    if( tm > data_next ){
        data_next = tm + data_to;
        rd.setPacketValue( tm );
        rd.sendPacket();
    }
}

Разберем программу по шагам. Первое, что мы делаем — это создаём объект класса SerialFlow:

SerialFlow rd(7,8);

7 и 8 — это номера выводов Ардуино, к которым подключены контакты радиомодуля CN и CSN.

В функции setup мы настраиваем формат передаваемых пакетов:

rd.setPacketFormat(2, 1);

Здесь первый аргумент — это размер передаваемых чисел. Помним, что число в диапазоне от 0 до 255 занимает 1 байт, а число от 0 до 65535 — 2 байта. Если мы будем в пакете передавать количество миллисекунд с начала запуска системы, то 2-х байтного числа нам хватит на 65 минут, что соответствует одному часу (вполне хватит для теста). После этого число переполнится и отсчет таймера сбросится. Второй аргумент этой функции отвечает за количество чисел в пакете. Нам будет достаточно одного числа, так сто ставим 1.

Теперь настроим передатчик. Первый аргумент — это адрес передатчика (модуль №1). Второй — адрес приёмника (модуль №2).

rd.begin(0xF0F0F0F0E1LL,0xF0F0F0F0D2LL);

Наконец, в цикле loop каждые 100 миллисекунд происходит отправка пакета. Здесь используется алгоритм выполнения по таймеру, который мы рассмотрели в одном из уроков. Переходим к приёмнику.

3. Программа для приемника

От приемника требуется принимать пакеты и каким-то образом сигнализировать нам об этом. Полученные данные будем отправлять через последовательный порт в монитор последовательного порта Arduino IDE.

#include <SerialFlow.h>

SerialFlow rd(7,8);

void setup(void){
    Serial.begin(57600);
    rd.setPacketFormat(2, 1);
    rd.begin(0xF0F0F0F0D2LL,0xF0F0F0F0E1LL);
}

void loop(void){
    unsigned int v;
    if( rd.receivePacket() ){
        v = rd.getPacketValue(0);
        Serial.println(v);
    }
}

Здесь мы точно так же создаем объект SerialFlow с аргументами 7 и 8. Затем настраиваем параметры пакета. А в строчке:

rd.begin(0xF0F0F0F0D2LL,0xF0F0F0F0E1LL);

первым параметром указываем адрес модуля №2, а вторым — адрес модуля №1.

Загружаем программу на оба контроллера и открываем на приемнике монитор последовательного порта. Если все было сделано правильно, то каждые 100 миллисекунд в окне должны появляться значения таймера на передатчике.

4. Передача данных с удаленных датчиков

Немного усложним схемы обоих устройств. На стороне передатчика №1 добавим два датчика, к примеру, датчик температуры LM35 и фоторезистор. На стороне приемника №2 подключим к Ардуино ЖК дисплей 1602. Дополненная программа для передатчика будет выглядеть следующим образом.

Программа для передатчика

#include <SerialFlow.h>

const byte temp_pin = A0;
const byte photo_pin = A1;
SerialFlow rd(7,8);

const unsigned long data_to = 100; 
unsigned long tm, data_next;

void setup(void){
    pinMode(temp_pin, INPUT);
    pinMode(photo_pin, INPUT);
    rd.setPacketFormat(2, 2);
    rd.begin(0xF0F0F0F0E1LL,0xF0F0F0F0D2LL);
}

void loop(void){
    tm = millis();
    if( tm > data_next ){
        data_next = tm + data_to;
        rd.setPacketValue( analogRead(temp_pin) );
        rd.setPacketValue( analogRead(photo_pin) );
        rd.sendPacket();
    }
}

Теперь мы передаем в пакете два числа в диапазоне от 0 до 1023, следовательно в формате пакета изменится только его размер.

Программа для приемника

Будем использовать ЖК дисплей 1602 с интерфейсом I2C. Для этого подключим библиотеку LiquidCrystal_I2C.

#include <SerialFlow.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);
SerialFlow rd(7,8);

void setup(void){
    rd.setPacketFormat(2, 2);
    rd.begin(0xF0F0F0F0D2LL,0xF0F0F0F0E1LL);

    lcd.init();
    lcd.backlight();
}

void loop(void){
    unsigned int v1,v2;
    if( rd.receivePacket() ){
        v1 = rd.getPacketValue(0);
        v2 = rd.getPacketValue(1);
        lcd.setCursor(0,0);
        lcd.print(v1);
        lcd.setCursor(8,0);
        lcd.print(v2);
    }
}

Здесь мы также меняем формат пакета, увеличивая его размер. При чтении чисел из пакета вызываем последовательно функции getPacketValue с аргументами 0 и 1.

Загружаем обе программы на соответствующие Ардуино и проверяем работу!

Задания

С помощью nRF24L01 можно немного автоматизировать быт. Вот пара заданий, которые позволят закрепить пройденный урок.

  1. Дистанционное реле для светильника. Требуется собрать пульт и исполнительное устройство, связанные радиоканалом. Пульт состоит из контроллера Ардуино, радиомодуля и одной тактовой кнопки. Исполнительное устройство состоит из контроллера Ардуино, радиомодуля и реле. При нажатии на кнопку пульта, необходимо отправлять сигнал на исполнительное устройство, которое переключит реле в нужное состояние и включит светильник.
  2. Пульт дистанционного управления телевизором. Необходимо собрать два устройства, которые позволят управлять телевизором или любым другим бытовым прибором с ИК пультом. Первое устройство — пульт, содержит: контроллер Ардуино, радиомодуль и три кнопки: «вкл/выкл», «канал+» и «канал-«. Второе устройство состоит из контроллера Ардуино, радиомодуля и ИК светодиода. При нажатии на одну из кнопок пульта, необходимо передавать на второе устройство команду. Второе устройство принимает команду и отправляет соответствующую управляющую последовательность импульсов на бытовой прибор с помощью ИК светодиода.

Заключение

Из-за своей дешевизны радиомодуль nRF24L01 можно встретить во множестве проектов, включая элементы умного дома и всевозможных самодельных роботов. Кто-то передает через nRF24 телеметрию с квадрокоптера, а кто-то делает на его основе «умные» розетки.

Справедливости ради, следует отметить, что кроме nRF24L01 существует еще много разных радиомодулей подобного класса. Ярким примером может являться CC2500. Также важно отметить, что nRF24L01 является устаревшей моделью, и имеется его более новая версия, которая является и более дорогой.

В одной из следующих статей мы рассмотрим радиомодуль nRF24L01 с усилителем, который позволяет передавать сигнала на расстояния до 2 км!


Изменено:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>