Подключение радио-модуля NRF24 к ардуино

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

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

Нередко возникают задачи, связанные с необходимостью получать данные от датчиков, находящихся на значительном удалении от управляющего устройства. Простое решение: протянуть пару десятков метров провода. Но в реальности это не всегда возможно. Длинные провода подвержены наводкам. Кроме того, подача питания по длинным проводам часто невозможна из-за высокого сопротивления проводника. Поэтому, источник питания всё равно приходится размещать в непосредственной близости от датчика. О необходимости прятать провода для минимальной эстетической привлекательности тоже не стоит забывать. Всё это подталкивает нас к идее обойтись вовсе без проводов, а именно построить систему передачи данных по радиоканалу. Существует и недорогое решение для беспроводной связи: радио-модуль на базе чипа NRF24. О подключении и работе с одной из разновидностей такого чипа и пойдёт речь в статье. Модуль стоит всего 1 доллар. По ссылке модуль можно купить с бесплатной доставкой у проверенного мной продавца. Или можно купить сразу десяток модулей. Тогда ещё дешевле.

Обзор радио-модуля NRF24L01

Модуль представляет собой полноценный микроконтроллер. Помимо программы, обеспечивающей работу с радио, в контроллер можно загрузить свою программу с помощью программатора. Однако, о том как это сделать я не буду рассказывать в этой статье. Модуль подключается к Arduino по интерфейсу SPI. Максимальная скорость обмена данными между модулями составляет 2Мбит/сек. Пожалуй, достаточно для любых задач. При подаче питания модули самостоятельно организуются в сеть. Управлять этим процессом мы не можем. Нам доступны команды вроде: «передай байт модулю с адресом 1», или «прочитай содержимое приёмного буфера». Вся низкоуровневая работа по передаче данных осуществляется модулем без участия внешнего контроллера. Каждому модулю должен быть назначен адрес. Адрес может занимать 3, 4 или 5 байт. Существует несколько библиотек для общения с модулем через Arduino. Отличаются они в основном уровнем общения. Где то вы пишете и читаете прямо из регистров контроллера модуля, где то работа с модулем осуществляется не так гибко, но в более привычном для ардуинщика виде на более высоком уровне. Мне нравится высокоуровневые библиотеки. В статье рассмотрена работа с помощью библиотеки Mirf. Но сначала, нужно подключить радиомодуль NRF24l01 к Arduino.

Подключение радио-модуля NRF24L01 к Arduino

Установить модуль на макетную плату невозможно из-за близко расположенных выводов. Для установки на макетную плату я изготовил переходники из кусочка макетного текстолита и пары гребёнок мама и папа. Подробно останавливаться на этом не буду, просто покажу коллаж с фотографиями переходника:

Переходник для установки модуля NRF24l01 на макетную плату

Переходник для установки модуля NRF24l01 на макетную плату

Но чаще я просто использую провода мама-мама и подключаю модуль к Arduino Nano. Для организации радиоканала потребуется как минимум 2 радио-модуля. Каждый радио-модуль подключается к контроллеру Ардуино с помощью интерфейса SPI.

Наиболее распространённые модули окрашены либо в зелёный, либо в чёрный цвет. Распиновка на зелёных модулях нанесена шелкографией, а вот на чёрных нет. Поэтому, вам пригодится картинка. Распиновка контактов выглядит так:

Распиновка модуля NRF24L01

Распиновка модуля NRF24L01

Обратите внимание! Контактов здесь меньше, чем на зелёном модуле за счёт того, что  VCC и GND не дублируются. На зелёном модуле вместо контакта GND расположен дублирующий контакт VCC. Поэтому, при замене чёрного модуля на зелёный без тщательной проверки подключения, можно легко устроить КЗ!

Зелёный модуль NRF24l01

Зелёный модуль NRF24l01

Как вы можете знать, интерфейс SPI на разных платах Arduino разведён по разному. На некоторых моделях (Mega2560, Nano, Uno) SPI пины продублированы на цифровых пинах платы. В таблице ниже приведено соответствие SPI контактам цифровых выводов:

Плата Arduino MOSI MISO SCK
Nano, Uno 11 12 13
Mega2560 51 50 52
Leonardo ICSP-4 ICSP-1 ICSP-3

В Arduino Leonardo пины SPI разъёма не дублируются на цифровые пины и подключить модуль можно только к SPI разъёму по следующей схеме:

ICSP разъём Arduino

ICSP разъём Arduino

Итак, выводы модуля подключаем к выводам платы в соответствии с таблицей. MOSI модуля к MOSI платы и т.д. Выводы модуля CE и CSN можно подключить к любому свободному цифровому выводу платы. Для удобства, подключаем CE к 8 цифровому выводу, а CSN к 7 выводу платы. Для питания модулю необходимо не 5 вольт, а 3,3 вольта. Несмотря на то, что в интернете множество информации о нормальной работе модуля при напряжения питания 5 вольт, я бы не стал запускать модуль с повышенным питанием на постоянной основе. Подавайте питание с разъёма Arduino 3,3V. Если используете arduino mini, на которой нет разъёма 3,3V, придётся использовать регулятор напряжения. Если уж совсем невмоготу, то подавайте 5 вольт на свой страх и риск. Никто вас за это не осудит. Для более стабильной работы модуля рекомендуется так же подключить между выводом +3,3В и землёй как можно ближе к самому модулю конденсатор не менее 10 мкф. Лучше всего припаять его прямо к пинам питания на модуле. Конденсатор не обязателен, но большинство тем на форумах с проблемами передачи данных с помощью радиомодуля заканчиваются сообщением: «я припаял конденсатор и теперь всё хорошо».

Модуль NRF24l01 с припаянным конденсаторомМодуль NRF24l01 с припаянным конденсатором

Модуль NRF24l01 с припаянным конденсатором

Итак, модуль подключен. Теперь самое интересное: программирование. Как отмечалось выше, для работы с модулем существует множество библиотек. В интернете вы можете найти множество споров о том, какая библиотека для работы с радио-модулем подходит больше всего. Мне нравится Mirf. С ней и работаю.

Краткий обзор библиотеки Mirf

Библиотеку можно скачать с официального сайта: playground.arduino.cc/InterfacingWithHardware/Nrf24L01
На всякий случай, я сохранил её у себя: http://uscr.ru/share/Mirf.zip

Распакуйте архив в папку libraries, которая находится в папке со средой программирования Arduino IDE.

Теперь, когда библиотека установлена, вы можете проверить работоспособность ваших модулей. Для этого в одну из плат с подключённым радио-модулем загрузите библиотечный пример ping_server, в другую — пример ping_client.

Или просто скопируйте вот эти скетчи:

ping_server:

ping_client:

При загрузке этих скетчей в платы, начнётся обмен данными. В монитор порта при этом будет выводиться информация о времени задержки при обмене пакетами в миллисекундах:

Успешный обмен пакетами между модулями NRF24L01

Успешный обмен пакетами между модулями NRF24L01

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

Передача полезных данных между двумя модулями

Рассмотренные выше примеры прекрасно демонстрируют саму возможность передачи данных, однако, не дают ответа на некоторые практические вопросы. Сейчас мы напишем прошивки для двух ардуин, одна из которых («сервер») будет уметь отвечать на 2 команды: отправить значение millis() и считать значение напряжения на пине A0 и отправить его по радиоканалу другой плате (клиент). На клиенте же мы реализуем таймаут ожидания ответа для того, что бы программа не зависла в случае недоступности сервера. Клиент будет просто в цикле запрашивать обе команды с сервера и ещё одну неподдерживаемую команду для демонстрации превышения таймаута ожидания ответа от сервера. Для понимания происходящего будет не лишним изучить такие понятия, как «указатель» и «ссылка» применительно к языку программирования. Но, по большому счёту, можно обойтись без такого знания. Честно говоря, я очень поверхностно разбираюсь в этом, поэтому некоторые моменты для меня являются магией. И всё равно не плохо живу при этом.

Важно! В скетчах в качестве пина для индикации используется пин 4. К нему можно подключить светодиод. Если захотите переназначить пин индикации на другой, сверьтесь с таблицей выше для того, что бы не занять случайно пин, используемый в SPI.

Код для сервера:

Код для клиента:

После загрузки в мониторе порта клиента будет нечто подобное:

Пример передачи данных с помощью радио модуля

Пример передачи данных с помощью радио модуля

При этом со стороны сервера:

Пример передачи данных с помощью радио модуля

Пример передачи данных с помощью радио модуля

Пришло время обратить внимание на некоторые детали.

Сразу поговорим о «магии»: приём и передачу данных лучше осуществлять через разные переменные. Команду кладёте в одну переменную, а результат принимаете в другую. В примере выше мы могли бы обойтись одной только переменной data, без command. Сначала приравнивать переменную data к единице и отправлять её, а затем в неё же принимать результат. Но тогда при передаче команды на приёмник приходит мусор. Очевидно, что проблема кроется в использовании ссылок, с которыми неплохо бы разобраться. Но я, как упоминал ранее, плохо разбираюсь в работе с ссылками, потому для меня это «магия». Ещё один магический ритуал — задержка (delay(10);). Именно при такой задержке модуль работает стабильно. Поставить задержку больше можно. А вот при меньшей задержке при попытке обратиться к модулю (проверить статус или сменить адрес) сразу после передачи пакета данных, модуль может зависнуть.

Пройдёмся по операторам:

Mirf.payload = PAYLOAD; устанавливает размер буфера для приёма «полезной нагрузки» из пакета данных. Мы его объявили равным размеру переменной типа unsigned long (4 байта) чуть выше: #define PAYLOAD sizeof(unsigned long). Вместо sizeof(unsigned long) можно было просто написать: «4». Но так проще. Payload сообщает модулю, размер пакетов, которые он должен использовать при передаче. При этом, вы не сможете передать больше данных, чем размер payload. Например, если бы мы установили payload равным 2 байтам, а передали 4, то на приёмнике в переменной оказалось бы случайное значение. При этом передать данных меньше, чем размер payload можно, но возможны трудноотлавливаемые баги. Возможно, это особенность библиотеки Mirf, но лучше устанавливать payload равным размеру передаваемых данных. Разумеется, payload должен совпадать для всех модулей, участвующих в передаче. Есть ограничение: payload не может быть больше 32 байт. При установке payload больше 32, вы получите множество забавных проблем. Если вам нужно передавать больше 32 байт данных за раз, то единственный путь — разработка собственного пакетного протокола, который будет «пилить» данные на пакеты по 32 байта и передавать их последовательно.

Mirf.setRADDR((byte*)’serv1′); задаёт адрес приёмника (R—receive, англ. приём). Именно под этим адресом модулю нужно отправлять пакеты от других модулей.

Mirf.setTADDR((byte *)’clie1′); задаёт адрес получателя (T-transmit, англ. передача). Общение между модулями строится по следующему сценарию: первый модуль единожды вызывает процедуру Mirf.setRADDR((byte*)’serv1′) и слушает эфир под адресом «serv1». Второй модуль единожды вызывает процедуру Mirf.setRADDR((byte*)’clie1′) и слушает эфир под адресом «clie1». При передаче пакета, на первом модуле устанавливается адресат Mirf.setTADDR((byte *)’clie1′) и пакет уходит второму модулю. Второй модуль для ответа устанавливает Mirf.setTADDR((byte *)’serv1′) и ответ уходит на первый модуль.
Важно! Адрес модуля должен занимать не менее 3 и не более 5 байт.

Mirf.isSending() проверяет, закончилась ли передача пакета. Буфер отправки и передачи не разделён. Поэтому, при должном везении, вы можете прочитать то, что пытаетесь отправить, если не будете проверять, закончилась ли уже отправка.

Mirf.dataReady() проверяет, что входящий пакет уже получен. Как отмечалось ранее, модули живут своей жизнью. В том числе и пакеты передаются между модулями без участия внешних устройств. Библиотека может лишь прочитать пришедший пакет.

Mirf.getData((byte *) &data); читает пришедший пакет в переменную data

Mirf.send((byte *) &data); отдаёт команду на отправку пакета. Обратите внимание, в вызове процедур указывается ссылка на переменную, а сами данные передаются в виде массива байт. В данном контексте сылку нужно понимать так: мы указываем библиотеке адрес в памяти, в котором лежит значение переменной. Библиотека принимает данные и копирует их в память. В результате, мы получаем доступ к переданному значению через переменную. Это удобно: вы можете передавать абсолютно любые данные. Главное, что бы типы данных совпали на приёмнике и на передатчике. Передавать таким образом можно даже структуры.

Передача структур

В структуру можно упихать много переменных и передать их все за раз. Это удобно, учитывая, что за раз мы можем передать 32 байта. Для начала напишем скетч, в котором научим arduino по команде «1» передавать команду дальше по цепочке. При этом каждый модуль будет знать только свой адрес и адрес следующего модуля в цепочке. Добавим для наглядности отчёт о получении команды (в ответ модуль будет отправлять число 42) и при этом я уберу проверку таймаута передачи, что бы не загромождать код. Для большей правдоподобности построим сеть из 3х модулей. При этом первый модуль будет у нас цепочку инициировать. Далее модули по кругу будут обмениваться командой «1».
Совершенно искусственный пример, как видите. Интерес в этом примере представляет пример передачи структур данных. Ну и поскольку модуль знает только следующего участника цепочки, но не знает предыдущего, нам понадобится передавать адрес отправителя прямо в структуре вместе с командой. Иначе модуль не будет знать, куда отправлять отчёт. Обратите внимание, как просто можно передавать строковые значения адреса модуля внутри структуры.
При работе со структурами очень важно правильно посчитать размер структуры и правильно указать PAYLOAD. Размер структуры равен размеру всех переменных, которые её составляют. Структуры, разумеется, тоже должны быть одинаковыми и на приёмнике и на передатчике.
Итак, вот такой скетч я написал. Скетч для всех модулей одинаков, меняются только некоторые переменные в начале.
Код для первого модуля:

Все остальные модули получают этот же скетч, меняются только три переменные вверху: ADDR, NEXT, iamfirst.
Для второго модуля будет так:

И, наконец, для третьего:

Третий модуль замкнёт цепочку и всё продолжится по кругу. Вот такой вывод в мониторе серийного порта должен появится (слева направо: первый, второй, третий модули):

Успешный обмен данными между тремя модулями

Успешный обмен данными между тремя модулями

Напоминаю, модуль NRF24L01 можно купить с бесплатной доставкой у проверенного мной продавца по этой ссылке. Ещё здесь можно заказать сразу десяток модулей. При покупке десяти стоимость одной штуки совсем смешная. Не забывайте, что я собираю большой список ссылок на продавцов, у которых я заказывал платы Arduino, модули, инструменты и просто полезные штуки и мне всё понравилось. Полный список здесь: Проверенные товары интернет-магазинов.

Кажется, это всё, что потребуется знать вам для передачи данных с помощью радио модуля NRF24. На все ваши вопросы с постараюсь ответить в комментариях.

Подключение радио-модуля NRF24 к ардуино: 3 комментария

  1. Frog163

    Привет, как быстро передаются данные через подобные модули? И еще, не нашел статьи, но в проверенных покупках есть датчики холла, могут ли они определить ротацию магнита? (знаю про As5045, но дороговат он).
    Спасибо за статью)

    1
    1. uscr Автор записи

      Сами модули могут передавать данные со скоростью до 2Мбит/сек.
      Датчик Холла может определить ротацию. Он аналоговый. По изменения выходного напряжения можно даже определить расстояние до магнита (после калибровки).

      0

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