В данной статье мы рассмотрим как построить беспроводную сеть на основе нескольких модулей приемопередатчиков NR24L01, управляться которые будут с помощью плат Arduino. В качестве примера мы рассмотрим создание подобной сети, состоящей из 5 узлов, каждый из которых сможет общаться с любым узлом в сети, и в то же время они могут работать как передатчики, так и приемники. Во многом это будет похоже на так называемую Mesh сеть. Максимально рассматриваемая нами сеть может содержать до 3125 модулей NR24L01, взаимодействующих друг с другом по одному радиочастотному каналу.
Ранее на нашем сайте мы уже рассматривали использование модулей NRF24L01 в следующих проектах:
- передача данных на смартфон с помощью Arduino, модуля NRF24L01 и Bluetooth (BLE);
- радиостанции большого радиуса действия на Arduino и модулях nRF24L01;
- пульт дистанционного управления дроном на Arduino;
- радиосвязь между Raspberry Pi и Arduino Uno с помощью модулей nRF24L01;
- подключение радиочастотного модуля nRF24L01 к микроконтроллеру PIC.
Но в отличие от данных проектов в этом проекте нам еще дополнительно понадобится библиотека RF24Network , которая позволяет достаточно просто построить беспроводную сеть Arduino с множеством плат, взаимодействующих друг с другом. Вот как работает топология сети.
Связь нескольких модулей NRF24L01
Один модуль NRF24L01 может активно прослушивать до 6 других подобных модулей одновременно.
Эта возможность используется библиотекой RF24Network для создания сети, организованной по топологии дерева, где один узел является базовым, а все остальные узлы являются дочерними элементами этого или другого узла. У каждого узла может быть до 5 дочерних элементов, и каждое такое поддерево может иметь глубину до 5 уровней, что означает, что мы можем создать сеть из 3125 узлов. Каждый узел должен быть определен с помощью 15-битного адреса, который точно описывает положение узла в дереве.
На самом деле мы можем определить адреса узлов в восьмеричном формате. Итак, адрес мастера или базы — 00, адреса базовых дочерних узлов — от 01 до 05, адреса дочерних узлов 01 — от 011 до 051 и так далее.
Обратите внимание, что если узел 011 хочет поговорить с узлом 02, связь должна будет проходить через узел 01 и базовый узел 00, поэтому эти два узла должны быть активны все время, чтобы связь была успешной.
Необходимые компоненты
- Плата Arduino Nano (купить на AliExpress).
- Радиочастотные модули nRF24L01 (купить на AliExpress).
- Серводвигатель (купить на AliExpress).
- Потенциометр (купить на AliExpress).
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Схема проекта
Прежде чем переходить к созданию сети из 5 модулей nRF24L01, рассмотрим более простой пример из двух плат Arduino, взаимодействующих друг с другом с помощью радиочастотных модулей nRF24L01. Схема данного примера показана на следующем рисунке.
В данной схеме мы, используя потенциометр на первой плате Arduino, будем управлять серводвигателем, подключенным ко второй плате Arduino. Давайте теперь посмотрим на исходные коды для этих двух частей проекта.
Код со стороны потенциометра:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
/* Arduino Wireless Network - Multiple NRF24L01 Tutorial == Example 01 - Servo Control / Node 00 - Potentiometer == by Dejan, www.HowToMechatronics.com Libraries: nRF24/RF24, https://github.com/nRF24/RF24 nRF24/RF24Network, https://github.com/nRF24/RF24Network */ #include <RF24.h> #include <RF24Network.h> #include <SPI.h> RF24 radio(10, 9); // nRF24L01 (CE,CSN) RF24Network network(radio); // Include the radio in the network const uint16_t this_node = 00; // Address of this node in Octal format ( 04,031, etc) const uint16_t node01 = 01; void setup() { SPI.begin(); radio.begin(); network.begin(90, this_node); //(channel, node address) } void loop() { network.update(); unsigned long potValue = analogRead(A0); // считываем значение с потенциометра unsigned long angleValue = map(potValue, 0, 1023, 0, 180); // конвертируем значение в диапазон 0-180 RF24NetworkHeader header(node01); // (Address where the data is going) bool ok = network.write(header, &angleValue, sizeof(angleValue)); // передаем данные } |
В представленном коде программы мы первым делом подключаем обе библиотеки RF24 и RF24Network, а также библиотеку SPI. Затем мы создаем объект RF24 и включаем его в объект RF24Network. Далее нам нужно назначить адреса узлов в восьмеричном формате - это будет 00 для первого узла и 01 для другого узла на стороне сервомотора.
После этого в функции setup() нам необходимо инициализировать сеть, установив канал и адрес этого узла.
Затем в функции loop() нам постоянно нужно вызывать функцию update(), через которую будут происходить все действия в сети. Затем мы считываем значение потенциометра и преобразуем его в диапазон от 0 до 180, подходящее для управления сервомотором. Далее мы создаем заголовок сети, где мы назначаем адрес узла, в который мы будем передавать данные. В конце с помощью функции write() мы отправляем данные на приемный узел. Итак, здесь первый параметр содержит информацию об адресах, второй параметр указывает, какие данные будут отправлены, а третий параметр — это размер данных.
Код на стороне сервопривода:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/* Arduino Wireless Network - Multiple NRF24L01 Tutorial == Example 01 - Servo Control / Node 01 - Servo motor == */ #include <RF24.h> #include <RF24Network.h> #include <SPI.h> #include <Servo.h> RF24 radio(10, 9); // nRF24L01 (CE,CSN) RF24Network network(radio); // Include the radio in the network const uint16_t this_node = 01; // Address of our node in Octal format ( 04,031, etc) Servo myservo; // create servo object to control a servo void setup() { SPI.begin(); radio.begin(); network.begin(90, this_node); //(channel, node address) myservo.attach(3); // (servo pin) } void loop() { network.update(); while ( network.available() ) { // если присутствуют какие либо данные на входе RF24NetworkHeader header; unsigned long incomingData; network.read(header, &incomingData, sizeof(incomingData)); // считываем поступившие данные myservo.write(incomingData); // даем команду сервомотору повернуться на заданный угол } } |
В приемной части нам необходимо подключить в программе те же самые библиотеки, которые мы подключали в передающей части, а также библиотеку для управления сервомотором. Адрес нашего приемного узла в восьмеричном формате — 01. Далее нам необходимо создать объект для управления сервомотором и инициализировать радиочастотный модуль.
Затем, в функции loop(), с помощью цикла while() и функции available() мы будем постоянно проверять есть ли какие-либо поступающие данные. Если да (true), то мы будем создавать объект, через который будут приниматься данные, а также переменную, в которой эти данные будут храниться. Затем с помощью функции read() мы будем считывать данные и сохранять их в переменной incomingData. В конце мы будем использовать эти данные для перемещения серводвигателя в соответствии с положением оси потенциометра на передающем узле.
Беспроводная сеть из нескольких модулей NRF24L01 под управлением
Arduino
Теперь рассмотрим создание сети из 5 узлов. Ее блок-схема показана на следующем рисунке.
В ней мы из корневого узла с помощью потенциометра будем управлять серводвигателем в узле 01, вторым потенциометром мы будем управлять светодиодами в узле 022, с помощью кнопки мы будем управлять светодиодом в узле 012, а светодиодом в корневом узле мы будем управлять с помощью потенциометра в узле 02. Также с помощью инфракрасного датчика в узле 012 мы будем управлять светодиодом в узле 01. Таким образом, мы сможем передавать и получать данные одновременно, а также взаимодействовать узлами из разных ветвей. Давайте теперь посмотрим на коды Arduino.
Исходный код для корневого узла (узел 00)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
/* Arduino Wireless Network - Multiple NRF24L01 Tutorial == Base/ Master Node 00== by Dejan, www.HowToMechatronics.com Libraries: nRF24/RF24, https://github.com/nRF24/RF24 nRF24/RF24Network, https://github.com/nRF24/RF24Network */ #include <RF24Network.h> #include <RF24.h> #include <SPI.h> #define button 2 #define led 3 RF24 radio(10, 9); // nRF24L01 (CE,CSN) RF24Network network(radio); // Include the radio in the network const uint16_t this_node = 00; // Address of this node in Octal format ( 04,031, etc) const uint16_t node01 = 01; // Address of the other node in Octal format const uint16_t node012 = 012; const uint16_t node022 = 022; void setup() { SPI.begin(); radio.begin(); network.begin(90, this_node); //(channel, node address) radio.setDataRate(RF24_2MBPS); pinMode(button, INPUT_PULLUP); pinMode(led, OUTPUT); } void loop() { network.update(); //===== Receiving =====// while ( network.available() ) { // Is there any incoming data? RF24NetworkHeader header; unsigned long incomingData; network.read(header, &incomingData, sizeof(incomingData)); // Read the incoming data analogWrite(led, incomingData); // PWM output to LED 01 (dimming) } //===== Sending =====// // Servo control at Node 01 unsigned long potValue = analogRead(A0); unsigned long angleValue = map(potValue, 0, 1023, 0, 180); // Suitable for servo control RF24NetworkHeader header2(node01); // (Address where the data is going) bool ok = network.write(header2, &angleValue, sizeof(angleValue)); // Send the data // LED Control at Node 012 unsigned long buttonState = digitalRead(button); RF24NetworkHeader header4(node012); // (Address where the data is going) bool ok3 = network.write(header4, &buttonState, sizeof(buttonState)); // Send the data // LEDs control at Node 022 unsigned long pot2Value = analogRead(A1); RF24NetworkHeader header3(node022); // (Address where the data is going) bool ok2 = network.write(header3, &pot2Value, sizeof(pot2Value)); // Send the data } |
В этом коде нам необходимо указать номера всех остальных узлов, на которые мы будем передавать данные с корневого узла. В функции loop() мы первым делом будем постоянно проверять наличие входящих данных. Если они присутствуют, то мы будем считывать их и сохранять их в переменной incomingData и затем использовать их для управления яркостью светодиода. Эти данные будут поступать на корневой узел от потенциометра из узла 02. После считывания значения потенциометра мы преобразуем его в диапазон от 0 до 255 (для ШИМ) и далее будем передавать его для управления светодиодом на другом узле.
Исходный код узла 02
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/* Arduino Wireless Network - Multiple NRF24L01 Tutorial == Node 02 (Child of Master node 00) == */ #include <RF24Network.h> #include <RF24.h> #include <SPI.h> RF24 radio(10, 9); // nRF24L01 (CE,CSN) RF24Network network(radio); // Include the radio in the network const uint16_t this_node = 02; // Address of our node in Octal format ( 04,031, etc) const uint16_t master00 = 00; // Address of the other node in Octal format const unsigned long interval = 10; //ms // How often to send data to the other unit unsigned long last_sent; // When did we last send? void setup() { SPI.begin(); radio.begin(); network.begin(90, this_node); //(channel, node address) radio.setDataRate(RF24_2MBPS); } void loop() { network.update(); //===== Sending =====// unsigned long now = millis(); if (now - last_sent >= interval) { // If it's time to send a data, send it! last_sent = now; unsigned long potValue = analogRead(A0); unsigned long ledBrightness = map(potValue, 0, 1023, 0, 255); RF24NetworkHeader header(master00); // (Address where the data is going) bool ok = network.write(header, &ledBrightness, sizeof(ledBrightness)); // Send the data } } |
Исходный код узла 01
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
/* Arduino Wireless Network - Multiple NRF24L01 Tutorial == Node 02 (Child of Master node 00) == */ #include <RF24Network.h> #include <RF24.h> #include <SPI.h> #include <Servo.h> #define led 2 RF24 radio(10, 9); // nRF24L01 (CE,CSN) RF24Network network(radio); // Include the radio in the network const uint16_t this_node = 01; // Address of our node in Octal format ( 04,031, etc) const uint16_t master00 = 00; // Address of the other node in Octal format Servo myservo; // create servo object to control a servo void setup() { SPI.begin(); radio.begin(); network.begin(90, this_node); //(channel, node address) radio.setDataRate(RF24_2MBPS); myservo.attach(3); // (servo pin) pinMode(led, OUTPUT); } void loop() { network.update(); //===== Receiving =====// while ( network.available() ) { // Is there any incoming data? RF24NetworkHeader header; unsigned long incomingData; network.read(header, &incomingData, sizeof(incomingData)); // Read the incoming data if (header.from_node == 0) { // If data comes from Node 02 myservo.write(incomingData); // tell servo to go to a particular angle } if (header.from_node == 10) { // If data comes from Node 012 digitalWrite(led, !incomingData); // Turn on or off the LED 02 } } } |
Узел 01 будет получать данные от двух разных узлов, от одного для управления сервомотором, а от другого для управления светодиодами - данный сигнал будет поступать от инфракрасного датчика узла 012.
Исходный код узла 012
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
/* Arduino Wireless Network - Multiple NRF24L01 Tutorial == Node 012 (child of Node 02)== */ #include <RF24Network.h> #include <RF24.h> #include <SPI.h> #define led 2 #define IR 3 RF24 radio(10, 9); // nRF24L01 (CE,CSN) RF24Network network(radio); // Include the radio in the network const uint16_t this_node = 012; // Address of our node in Octal format ( 04,031, etc) const uint16_t node01 = 01; // Address of the other node in Octal format void setup() { SPI.begin(); radio.begin(); network.begin(90, this_node); //(channel, node address) radio.setDataRate(RF24_2MBPS); pinMode(led, OUTPUT); pinMode(IR, INPUT); } void loop() { network.update(); //===== Receiving =====// while ( network.available() ) { // Is there any incoming data? RF24NetworkHeader header; unsigned long buttonState; network.read(header, &buttonState, sizeof(buttonState)); // Read the incoming data digitalWrite(led, !buttonState); // Turn on or off the LED } //===== Sending =====// unsigned long irV = digitalRead(IR); // Read IR sensor RF24NetworkHeader header8(node01); bool ok = network.write(header8, &irV, sizeof(irV)); // Send the data } |
В данном случае мы используем атрибут header.from_node, чтобы получить информацию из какого узла поступают данные. Если данные поступают от мастера (корневого узла), мы используем их для управления сервомотором, а если данные поступают от узла 012, мы используем их для управления светодиодом.
В узле 012 у нас есть и передача, и прием данных. Инфракрасный датчик управляет ранее упомянутым светодиодом в узле 01, а светодиод здесь управляется кнопкой на корневом узле.
Исходный код узла 022
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
/* Arduino Wireless Network - Multiple NRF24L01 Tutorial == Node 022 (child of Node 02)== */ #include <RF24Network.h> #include <RF24.h> #include <SPI.h> #define led1 2 #define led2 3 #define led3 4 #define led4 5 RF24 radio(10, 9); // nRF24L01 (CE,CSN) RF24Network network(radio); // Include the radio in the network const uint16_t this_node = 022; // Address of our node in Octal format ( 04,031, etc) const uint16_t master00 = 00; // Address of the other node in Octal format void setup() { SPI.begin(); radio.begin(); network.begin(90, this_node); //(channel, node address) radio.setDataRate(RF24_2MBPS); pinMode(led1, OUTPUT); pinMode(led2, OUTPUT); pinMode(led3, OUTPUT); pinMode(led4, OUTPUT); } void loop() { network.update(); //===== Receiving =====// while ( network.available() ) { // Is there any incoming data? RF24NetworkHeader header; unsigned long potValue; network.read(header, &potValue, sizeof(potValue)); // Read the incoming data // Turn on the LEDs as depending on the incoming value from the potentiometer if (potValue > 240) { digitalWrite(led1, HIGH); } else { digitalWrite(led1, LOW); } if (potValue > 480) { digitalWrite(led2, HIGH); } else { digitalWrite(led2, LOW); } if (potValue > 720) { digitalWrite(led3, HIGH); } else { digitalWrite(led3, LOW); } if (potValue > 960) { digitalWrite(led4, HIGH); } else { digitalWrite(led4, LOW); } } } |
Наконец, светодиоды в узле 022 управляются с использованием данных, поступающих от другого потенциометра на корневом узле.
Итак, в качестве вывода можно отметить, что если все подключено правильно и все узлы постоянно активны, то наша работа сводится к точной адресации узлов, а всю тяжелую работу выполняет потрясающая библиотека RF24Network.
Как Вы насчитали 3125 модулей ?
Честно говоря, считал не я, а автор статьи - я ее только перевел. Но неужели вы и вправду собираетесь создать сеть с таким большим числом узлов?