При создании некоторых проектов, требуется разделить выполняемые задачи между несколькими arduino.
В этом уроке мы научимся соединять несколько arduino по радиоканалу ISM диапазона, используя радио модуль nRF24L01+, на расстоянии до 100 м. Если использовать радио модули NRF24L01+PA+LNA, то расстояние между arduino можно увеличить до 1 км, не меняя код скетча.
Преимущества:
- Отсутствие проводов между arduino.
- Высокая скорость передачи данных, до 2 Мб/с. Выше чем у шин I2C и UART.
- Полудуплексная связь. Режим работы модулей (приёмник / передатчик) можно менять в процессе их работы.
- Высокая помехозащищенность. Данные в пакетах принимаются с проверкой CRC.
- Контроль доставки данных. Приемник отправляет передатчику сигнал подтверждения приёма данных (без смены режима работы).
- Возможность выбора одного из 128 каналов связи. Шаг каждого канала равен 1 МГц (от 2,400 ГГц до 2,527 ГГц).
- Возможность одновременной работы до 6 передатчиков на одном канале.
Недостатки:
- Модули nRF24L01+ работают в радиочастотном диапазоне ISM (Industrial, Scientific, Medical) 2,4 ГГц, на котором работают WiFi, Bluetooth и другие устройства, например радио телефоны и даже СВЧ печи. Эти устройства могут «глушить» некоторые каналы данного диапазона. Поэтому вблизи таких устройств дальность связи между модулями, на некоторых каналах, резко уменьшается. Увеличить дальность можно сменив канал связи на любой из 128 доступных модулям nRF24L01+.
- При выборе скорости 2 Мб/с, задействуются сразу два канала (выбранный и следующий за ним).
- Модули питаются от напряжения 3,3 В постоянного тока. Но их можно запитать от 5 В через адаптер nRF24L01+.
Нам понадобится:
- Радио модуль nRF24L01+ х 3шт.
- Адаптер к модулю nRF24L01+ х 3шт.
- Arduino х 3шт.
- Trema Shield х 3шт.
- Trema Slider х 1шт.
- Trema потенциометр х 1шт.
- Trema четырехразрядный LED индикатор х 1шт.
- Сервопривод x 1шт.
- Набор проводов «мама-мама» для подключения радио модулей х 1 комплект.
Для реализации проекта нам необходимо установить библиотеки:
- Библиотека RF24 (для работы с радио модулями nRF24L01+).
- Библиотека iarduino_4LED, (для работы с Trema четырехразрядным LED индикатором).
- Библиотеки SPI и Servo входят в стандартный набор Arduino IDE.
О том как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE.
Видео:
Схема подключения:
Все радио модули nFR24L01+ подключены, через адаптер, к аппаратной шине SPI. Trema четырехразрядный LED индикатор подключён к цифровым выводам D2 и D3 (можно подключить к любым выводам Arduino). Сервопривод подключён к цифровому выводу D4 (можно подключить к любым выводам). Trema потенциометр и слайдер подключены к аналоговым входам A0 (можно подключить к любым аналоговым входам). Питание адаптера nFR24L01+ взято с контактов GND и Vcc (5 В).
Если Вы будете подключать модуль nFR24L01+ без адаптера, то модуль требуется запитать от напряжения 3,3 В постоянного тока.
Таблица подключения радио модуля nFR24L01+
Адаптер nRF24L01+ | Arduino Uno | Назначение |
---|---|---|
CE / SS | 9 (меняется в скетче) | Шина SPI - выбор устройства |
CSN | 10 (меняется в скетче) | Выбор режима: приёмник / передатчик |
SСK | 13 (SCK) | Шина SPI - линия тактирования |
MO | 11 (MOSI) | Шина SPI - линия данных (от мастера к ведомому) |
MI | 12 (MISO) | Шина SPI - линия данных (от ведомого к мастеру) |
IRQ | Не используется | Прерывание |
Алгоритм работы:
Передатчики:
Скетчи обоих передатчиков идентичны, за исключением значения идентификатора трубы, которое должно быть уникальным для каждого передатчика, работающего на одном канале.
При старте (в коде setup) скетч настраивает работу радио модуля в режим передачи данных, указывая номер канала, скорость передачи, мощность передачи и идентификатор трубы.
Далее, каждый передатчик постоянно (в коде loop), считывает показания с аналогового входа A0 в 0 элемент массива data и отправляет этот массив радио модулю для передачи. Код loop выполняется с задержкой в 50 мс, этого времени достаточно для того что бы приемник успел принять и обработать данные другого передатчика.
Приёмник:
При старте (в коде setup) скетч настраивает работу радио модуля в режим приёма данных, указывая те же данные, что и у обоих передатчиков: номер канала, скорость передачи данных, мощность передачи. Но, в отличии от передатчиков, создаёт (открывает) не одну, а две трубы для приёма данных, указывая 1 трубе идентификатор 1 передатчика, а 2 трубе идентификатор 2 передатчика.
Далее приёмник инициирует работу с LED индикатором и сервоприводом. После чего, постоянно (в коде loop), проверяет нет ли в буфере данных, принятых радио модулем. Если данные есть, то они читаются в массив data, а в переменную pipe читается номер трубы, по которой эти данные пришли, таким образом определяя от какого передатчика приняты данные.
- Если данные приняты от передатчика №1, то значение 0 элемента массива data (показания Trema потенциометра) преобразуются в градусы и используется для поворота сервопривода.
- Если данные приняты от передатчика №2, то значение 0 элемента массива data (показания Trema слайдера) выводятся на LED индикатор.
Идентификаторы труб:
- Приемнику можно задать до 6 труб с номерами от 0 до 5. Каждой трубе присваивается идентификатор передатчика, данные которого требуется принимать.
- Значение идентификатора 0 трубы может отличаться от остальных идентификаторов всеми байтами.
- Значения идентификаторов 1-5 труб должны отличаться друг от друга только младшим байтом.
Код программы:
Передатчик №1:
#include <SPI.h> // Подключаем библиотеку для работы с шиной SPI #include <nRF24L01.h> // Подключаем файл настроек из библиотеки RF24 #include <RF24.h> // Подключаем библиотеку для работы с nRF24L01+ RF24 radio(9, 10); // Создаём объект radio для работы с библиотекой RF24, указывая номера выводов nRF24L01+ (CE, CSN) int data[1]; // Создаём массив для передачи данных (так как мы будем передавать только одно двухбайтное число, то достаточно одного элемента массива типа int) // Для данного примера, можно использовать не массив data из одного элемента, а переменную data типа int void setup(){ radio.begin(); // Инициируем работу nRF24L01+ radio.setChannel(5); // Указываем канал передачи данных (от 0 до 127), 5 - значит передача данных осуществляется на частоте 2,405 ГГц (на одном канале может быть только 1 приёмник и до 6 передатчиков) radio.setDataRate (RF24_1MBPS); // Указываем скорость передачи данных (RF24_250KBPS, RF24_1MBPS, RF24_2MBPS), RF24_1MBPS - 1Мбит/сек radio.setPALevel (RF24_PA_HIGH); // Указываем мощность передатчика (RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBm, RF24_PA_MAX=0dBm) radio.openWritingPipe (0xAABBCCDD11LL); // Открываем трубу с идентификатором 0xAABBCCDD11 для передачи данных (на одном канале может быть открыто до 6 разных труб, которые должны отличаться только последним байтом идентификатора) } void loop(){ data[0] = analogRead(A0); // считываем значение с аналогового входа A0 radio.write(&data, sizeof(data)); // отправляем данные из массива data указывая сколько байт массива мы хотим отправить. Отправить данные можно с проверкой их доставки: if( radio.write(&data, sizeof(data)) ){данные приняты приёмником;}else{данные не приняты приёмником;} delay(50); // устанавливаем задержку на 50 мс, за это время приемник успеет принять и обработать данные этого и другого передатчика. }
Передатчик №2:
#include <SPI.h> // Подключаем библиотеку для работы с шиной SPI #include <nRF24L01.h> // Подключаем файл настроек из библиотеки RF24 #include <RF24.h> // Подключаем библиотеку для работы с nRF24L01+ RF24 radio(9, 10); // Создаём объект radio для работы с библиотекой RF24, указывая номера выводов nRF24L01+ (CE, CSN) int data[1]; // Создаём массив для передачи данных (так как мы будем передавать только одно двухбайтное число, то достаточно одного элемента массива типа int) // Для данного примера, можно использовать не массив data из одного элемента, а переменную data типа int void setup(){ radio.begin(); // Инициируем работу nRF24L01+ radio.setChannel(5); // Указываем канал передачи данных (от 0 до 127), 5 - значит передача данных осуществляется на частоте 2,405 ГГц (на одном канале может быть только 1 приёмник и до 6 передатчиков) radio.setDataRate (RF24_1MBPS); // Указываем скорость передачи данных (RF24_250KBPS, RF24_1MBPS, RF24_2MBPS), RF24_1MBPS - 1Мбит/сек radio.setPALevel (RF24_PA_HIGH); // Указываем мощность передатчика (RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBm, RF24_PA_MAX=0dBm) radio.openWritingPipe (0xAABBCCDD22LL); // Открываем трубу с идентификатором 0xAABBCCDD22 для передачи данных (на одном канале может быть открыто до 6 разных труб, которые должны отличаться только последним байтом идентификатора) } void loop(){ data[0] = analogRead(A0); // считываем значение с аналогового входа A0 radio.write(&data, sizeof(data)); // отправляем данные из массива data указывая сколько байт массива мы хотим отправить. Отправить данные можно с проверкой их доставки: if( radio.write(&data, sizeof(data)) ){данные приняты приёмником;}else{данные не приняты приёмником;} delay(50); // устанавливаем задержку на 50 мс, за это время приемник успеет принять и обработать данные этого и другого передатчика. }
Приемник:
#include <SPI.h> // Подключаем библиотеку для работы с шиной SPI #include <nRF24L01.h> // Подключаем файл настроек из библиотеки RF24 #include <RF24.h> // Подключаем библиотеку для работы с nRF24L01+ #include <iarduino_4LED.h> // Подключаем библиотеку для работы с четырёхразрядным LED индикатором #include <Servo.h> // Подключаем библиотеку для работы с сервоприводами RF24 radio(9, 10); // Создаём объект radio для работы с библиотекой RF24, указывая номера выводов nRF24L01+ (CE, CSN) iarduino_4LED dispLED(2,3); // Создаём объект dispLED для работы с функциями библиотеки iarduino_4LED, с указанием выводов дисплея ( CLK , DIO ) Servo myservo; // Создаём объект myservo для работы с функциями библиотеки Servo int data[1]; // Создаём массив для приёма данных (так как мы будем принимать от каждого передатчика только одно двухбайтное число, то достаточно одного элемента массива типа int) uint8_t pipe; // Создаём переменную для хранения номера трубы, по которой пришли данные // Для данного примера, можно использовать не массив data из одного элемента, а переменную data типа int void setup(){ delay(1000); myservo.attach(4); // Подключаем объект myservo к 4 выводу Arduino dispLED.begin(); // Инициируем работу индикатора radio.begin(); // Инициируем работу nRF24L01+ radio.setChannel(5); // Указываем канал приёма данных (от 0 до 127), 5 - значит приём данных осуществляется на частоте 2,405 ГГц (на одном канале может быть только 1 приёмник и до 6 передатчиков) radio.setDataRate (RF24_1MBPS); // Указываем скорость передачи данных (RF24_250KBPS, RF24_1MBPS, RF24_2MBPS), RF24_1MBPS - 1Мбит/сек radio.setPALevel (RF24_PA_HIGH); // Указываем мощность передатчика (RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBm, RF24_PA_MAX=0dBm) radio.openReadingPipe (1, 0xAABBCCDD11LL); // Открываем 1 трубу с идентификатором 1 передатчика 0xAABBCCDD11, для приема данных radio.openReadingPipe (2, 0xAABBCCDD22LL); // Открываем 2 трубу с идентификатором 2 передатчика 0xAABBCCDD22, для приема данных radio.startListening (); // Включаем приемник, начинаем прослушивать открытые трубы // radio.stopListening (); // Выключаем приёмник, если потребуется передать данные } void loop(){ if(radio.available(&pipe)){ // Если в буфере имеются принятые данные, то получаем номер трубы, по которой они пришли, по ссылке на переменную pipe radio.read(&data, sizeof(data)); // Читаем данные в массив data и указываем сколько байт читать if(pipe==1){myservo.write(map(data[0],0,1023,0,180));} // Если данные пришли от 1 передатчика, то поворачиваем сервопривод на угол заданный Trema потенциометром if(pipe==2){dispLED.print(data[0]);} // Если данные пришли от 2 передатчика, то выводим показания Trema слайдера на индикатор } }
Обсуждение