nRF24L01+ : побеждаем модуль.

Тема в разделе "Проводная и беспроводная связь", создана пользователем ИгорьК, 19 июн 2014.

  1. ИгорьК

    ИгорьК Давно здесь

    Если вы собрали нечто на этом модуле, убедились что соединения правильные, а оно не работает:

    1. Не надо бросаться читать эту тему.

    2. Запитайте модуль от отдельного источника 3.3 вольта мощностью не менее 300 ма - и будет вам счастье. Нет? Припаяйте конденсатор 2-10 мкф между плюсом и минусом на модуле. Нет? Параллельно припаяйте керамический конденсатор. Верится с трудом? Жмем сюда.

    У вас Arduino Mega? Еще раз читаем и перечитываем этот пункт!

    Нет? Смотрим дальше...

    3. Есть серьезные основания полагать, что у Вас лично, да-да, у Вас, не настоящий модуль, а его клон. Клоны, при инициализации,

    (1) требуют установления абсолютно всех возможных параметров идентичными. По умолчанию они устанавливаются разными значениями. (Или вообще инициализируются рандомно, если прямо не указаны - есть такие подозрения.)
    С библиотеками идут примеры их использования. Обычно, в setup() автор устанавливает лишь основные моменты для решения задачи примера. В большинстве случаев это работает, так как вы покупаете модули одной партии. Но бывают случаи, когда примеры не работают, а еще "страннее" - одни примеры работают, а другие - нет. В такой ситуации можно впасть в ступор. (По крайней мере у меня было такое состояние.)
    Решение, оказывается, простое - разобраться с каждой настройкой, которая автором библиотеки указана дефолтной и необязательной.
    У меня, например, модули устойчиво заработали, когда была выполнена одинаковая установка функции setCRCLength().

    Таким образом, внимательно читать все, что указано как optional в библиотеках
    (2) лучше устанавливать в скорость передачи 1 Мбит.
    (3) менять параметры ASK на удачу.
    (4) проверять работу при подтяжке CS к питанию.
    (5) Если у вас Малина и вы любите Python - смотреть сюда.
    Удивительный совет от sv18sv.
    ======================================
    6. Если счастья не случилось, тему придется читать.
    Но читать начинайте не сначала, а вот с этого сообщения. И здесь тоже очень полезная информация для прояснения обстановки: кое-что надо будет отключить в модуле.
    ======================================



    Ок, начнем повесть... <= Жмем сюда, чтобы пропустить лирическое введение.

    Три! Целых три недели потребовалось для того, чтобы снять этот ролик! На нем вот что.
    Две Ардуинки с модулями nRF24L01+ сидят на шкафах и синхронно включают и выключают цветную ленту, лежащую на карнизах шкафов:
    - включена полностью;
    - лампа настроения;
    - ночная подсветка.​
    Обе Ардуинки получают команды от третьей по воздуху через nRF24L01+. Работа идет в рамках вот этого проекта. И хотя проект еще не закончен, мучения с nRF24L01+ уже требуют выговориться на ее тему.
    А пока видео:

     
    Последнее редактирование: 23 ноя 2016
    Neu, Папа, Henry_Ford и 6 другим нравится это.
  2. ИгорьК

    ИгорьК Давно здесь

    Для самых нетерпеливых: чтобы заставить этот модуль работать четко (в предыдущем посте видно, что пара модулей параллельно безошибочно отрабатывает 12 команд подряд), пришлось сделать две вещи:
    - припаять конденсатор 1-2 мкф между выводами плюс и минус nRF24L01+ прямо не его плату; Вот здесь сказано почему это. Вообще, перед первой командой инициализацией модуля не мешает сделать паузу в пару секунд после подачи питания.
    plata02.jpg
    - сделать публичной функцию RF24::flush_tx в библиотеке RF24 и "сливать" все что есть в передающем буфере перед отправкой нового сообщения.
    И еще раз кратко:

    1. Проверка соединения.
    2. Качественное питание: отсутствие пульсаций на БП и непросадка при работе. Сколько надо тока? Не знаю. Чем больше тем лучше. Но 5 А - это перебор. Хотя хуже не будет.
    3. Напайка керамических конденсаторов.
    4. Полная инициализация.
    5. Отключение AСK.
    Код (C++):
    setAutoAck(false);
    Или наоборот включение
    Код (C++):
    setAutoAck(true);
    которое на самом деле выключает. Все зависит от конкретного модуля и подбирается методом инженерного тыка.

    Не помогло? - выбрасывать.

    Для тех кто менее спешит, в ближайшее время несколькими постами обобщу свой опыт работы.
     
    Последнее редактирование: 28 май 2016
    Neu, Henry_Ford, TEXTRON и 3 другим нравится это.
  3. acos

    acos Официальный гик Команда форума

    Уау! Красотища!
    А в чём была проблема, что пришлось добавлять конденсатор?
     
  4. ИгорьК

    ИгорьК Давно здесь

    Проблема в том, что:
    - в Ардуинке очень слабый ПО ФАКТУ питатель 3.3 вольта и он просаживается на отдельных платах при работе с модулем.
    - на нем же, возникают сильные наводки: у меня на плате три MOSFET транзистора, без конденсатора "убивают" модуль наповал.
    Особенно проблема характерна для Меги (на фото), которая выступает на управляющей стороне.
    Спаял два абсолютно одинаковых (приемных) шилда. Так вот, один шилд работал через пень - колода, а второй практически не работал. Глаза сломал - искал может не пропаял где-то или замкнул что-то.
    При совместном включении вся система вела себя вообще странным образом. В общем, наложилось все сразу - проблемы железа (а это три модуля: передатчик и два приемника), библиотеки, и ошибки моего программирования.
    Причем, при супернестабильной работе железа отлавливать ошибки в программировании и сложно и даже желание пропадает. Уже хотел бросить все.
    На видео выше передатчик тупо с интервалом посылает в эфир четыре команды. Видели бы Вы как вела себя эта же система первое время...
    Припаял конденсаторы. Стало лучше. Второй шилд заработал. Но все равно, система явно бредила... В общем, путем бесчисленных игр с трубами (pipe - термин модуля), размерами буферов и номерами каналов, удалось понять логику ошибок.
    И как только "рассекретил" функцию очистки буфера передатчика - все заработало как часы.
    =============================================================
    Добавочка от 25.06.2014.
    Перенес ардуинку, что работала на правом шкафу, не тот шкаф что прямо и на видео не подсвечивается. Там ленты в два раза больше.
    И устройство отказалось работать. Два канала ленты еще тянет, а три - нет.
    То есть при включенных трех каналах ленты устройство стартует, тестирует их - все зажигается. Но модуль отказывается подавать признаки жизни. Отключаю один канал ленты - все работает. Что буду делать... Буду делать отдельный блок транзисторов, выносить за пределы шилда.

    ... и это - помогло. Модуль с Ардуиной и блок транзисторов соединены кусочками шлейфов и находятся друг от друга на расстоянии около 10 см.
     
    Последнее редактирование: 20 ноя 2014
    Henry_Ford нравится это.
  5. ИгорьК

    ИгорьК Давно здесь

    Итак, начнем (уже припаяли конденсатор?):
    - с соединения (все правильно соединили?),
    - проверки работоспособности (а модуль то работает?);
    - нахождения правильного канала для связи, коих в модуле есть 127, по умолчанию модуль работает на 76(hex).
    Соединяем так:
    24L01Pinout-800.jpg
    12345.jpg
    А потом устанавливаем библиотеку и загружаем скетч, который я чуть модифицировал, избавив от файла "printf.h" и добавив в начало функцию, которая изучает содержимое регистров модуля :
    Код (C):
    #include <SPI.h>
    #include "nRF24L01.h"
    #include "RF24.h"

    RF24 radio(9,10); // Для Уно
    //RF24 radio(9,53);// Для Меги
    const uint8_t num_channels = 128;
    uint8_t values[num_channels];
    void setup(void)
    {
      Serial.begin(57600);
      printf_begin();
      radio.begin();
      radio.setAutoAck(false);
      radio.startListening();

      radio.printDetails();  // Вот эта строка напечатает нам что-то, если все правильно соединили.
      delay(5000);              // И посмотрим на это пять секунд.

      radio.stopListening();
      int i = 0;    // А это напечатает нам заголовки всех 127 каналов
      while ( i < num_channels )  {
        printf("%x",i>>4);
        ++i;
      }
      printf("\n\r");
      i = 0;
      while ( i < num_channels ) {
        printf("%x",i&0xf);
        ++i;
      }
      printf("\n\r");
    }
    const int num_reps = 100;

    void loop(void)
    {
      memset(values,0,sizeof(values));
      int rep_counter = num_reps;
      while (rep_counter--) {
        int i = num_channels;
        while (i--) {
          radio.setChannel(i);
          radio.startListening();
          delayMicroseconds(128);
          radio.stopListening();
          if ( radio.testCarrier() )
            ++values[i];
        }
      }
      int i = 0;
      while ( i < num_channels ) {
        printf("%x",min(0xf,values[i]&0xf));
        ++i;
      }
      printf("\n\r");
    }
    int serial_putc( char c, FILE * ) {
      Serial.write( c );
      return c;
    }

    void printf_begin(void) {
      fdevopen( &serial_putc, 0 );
    }
     
    Таким образом, модуль отвечает на наши вопросы, а потом слушает эфир и расскажет нам о некоторых полезных вещах.
     
    Последнее редактирование: 14 янв 2016
    Neu, Henry_Ford, greytm и 7 другим нравится это.
  6. ИгорьК

    ИгорьК Давно здесь

    Первая полезная вещь - статус платы, и, следовательно, успешность соединения с ней и ее работоспособности. В порту мы должны получить изображение типа этого:
    0003.jpg
    Если не получили - паяльник и увеличительное стекло в руки - ищем ошибки соединения.
     
    Последнее редактирование: 20 июн 2014
    Neu, Stas Di, Henry_Ford и 3 другим нравится это.
  7. ИгорьК

    ИгорьК Давно здесь

    1. Потому что мы не ищем легких путей, и эта библиотека мне больше понравилась. Надеюсь, понравится и Вам.
    2. Потому что эта библиотека снабжена большим количеством примеров. Mirf абсолютно "голая", без пояснений. Смог разобраться в ней лишь после изучения этой библиотеки.
    3. Потому что она, по утверждению отдельных граждан в сети, обладает бОльшими возможностями и универсальностью. В частности, позволяет в ответ на запрос автоматом проталкивать заранее заготовленный ответ, что может быть полезно при опросе датчиков без построения сети типа XBee.
    4. Потому что эта библиотека лежит в основе другой библиотеки, которая позволяет строить сеть типа XBee, но за цену модулей nRF24L01+.
     
    Последнее редактирование: 24 июн 2014
    Megakoteyka нравится это.
  8. ИгорьК

    ИгорьК Давно здесь

    Вторая полезная вещь выглядит вот так:
    0004.jpg
    В первой строке - первая цифра номера канала, во второй строке - вторая (hex). А ниже - уровень зашумленности. В данном случае каналы 39-42 использовать не рекомендуется. Ну и 49 -4а тоже. У вас обязаны быть другие результаты.
     
    Последнее редактирование: 20 июн 2014
    Neu, Maksim_Z, altex и 2 другим нравится это.
  9. Megakoteyka

    Megakoteyka Оракул

    Отличная работа! Спасибо.
    acos, тащите это в вики ;)
    Кстати, когда там уже появится давно обещанный раздел для пользователей/песочница?
     
    Alex469 нравится это.
  10. ИгорьК

    ИгорьК Давно здесь

    Это еще не конец :) Надеюсь, Амперка нальет мне немного за продвижение ее продуктов :rolleyes:
     
    cody_c++ и zaynus нравится это.
  11. ИгорьК

    ИгорьК Давно здесь

    Теперь несколько слов о самом модуле (спасибо тому (Пушной звер), кто отговорил меня пользоваться модулями на 433 МГц). Вот официальная документация о 78 страницах. Как обычно бывает, когда кто-то написал для нас библиотеку, из нее, в этом случае, достаточно хорошо понять возможности модуля, а затем искать их реализацию в библиотеке.
    Попробую изложить все несколькими тезисами.
    - Работа по шине SPI, которая поддерживается Ардуино на уровне железа и соответствующей библиотеки.
    - 3.3 вольта на питание. 5 Ардуиновских, обычно, выдерживает, не сгорает, но и не работает. Экспериментировать не рекомендуется.
    - три скорости передачи, причем на медленной - выше чувствительность. По умолчанию установлена средняя скорость.
    - несколько уровней мощности передачи;
    - передача в буфер (за раз, за сеанс) до 32 байт. Регулируемая величина. В интернете видел, что ее уменьшение повышает стабильность связи, но... НО см. посты выше. Моя практика это не подтвердила.
    - прослушка одновременно шести pipe (труба?). То есть может одновременно ловить сигнал от шести таких же модулей, передающих на одном частотном канале;
    - передача (без перенастройки! перенастроил - и хоть сотня, в первом посте передача идет по двум разным трубам! ) - по одной трубе/pipe;
    - включаемый/выключаемый режим контроля получения адресатом сообщения;
    - в этом режиме задаются количество попыток и интервал "добивания" приемника на предмет получения сообщения;
    - при включенном режиме контроля полученного сообщения, приемник может в обратку отправить заранее заготовленную информацию.​
    Этот последний режим, ИМХО, очень интересен для организации опроса датчиков, например, температуры. Ведомый периодически измеряет температуру и отправляет ее к себе в буфер - в эфир ничего не говорит, не засоряет. Ведущий обращается когда ему надо к ведомому с любой командой, а в ответ принимает температуру, или вообще любую другую информацию. При такой работе количество прослушиваемых неограниченно.
    Иной способ - отключить систему оповещения о приеме сообщения и "в лоб" давать запросы на ведомого, вынуждая его к ответу в другой "трубе".
    Третий способ - вообще молчать и слушать только шесть труб. Однако, подозреваю, в случае одновременной передачи двумя модулями может произойти что-то нехорошее.
     
    Последнее редактирование: 20 июн 2014
  12. Пушной звер

    Пушной звер Оракул

    я тогда еще пофлужу, на 433 есть нормальные модули, вроде на SI4432, но опятьже использовать их имеет смысл для дальнобоя, ибо в комнате жарить ими ну смысл? особенно когда есть куча вариантов на 2,4 без головной боли.
     
  13. Megakoteyka

    Megakoteyka Оракул

    А на километры открытой местности посоветуете что-нибудь? Важна дальность, а не скорость.
     
  14. Пушной звер

    Пушной звер Оракул

    на километры можно все те же SI4432 или nRF24L01+ но с усилком и правильной антенной, вот к примеру первый попавшийся http://www.ebay.com/itm/RF-Wireless...ion_Controls_Touchscreens&hash=item460e8a3f2b пол ватта на 433, а nRF24L01+ с услом на 100 милливат и антенной ~16дб, думаю и на 10км долбануть не проблема будет http://www.ebay.com/itm/NRF24L01-PA...557?pt=LH_DefaultDomain_0&hash=item485448cd1d
    SMA там есть, даже паять нечего не надо.
     
  15. Megakoteyka

    Megakoteyka Оракул

    Я что-то путаю или у нас излучать разрешается только 20 мВт?
    Если так, то пол ватта перебор...
    Или запрет только на закрытые диапазоны работает?
     
  16. ИгорьК

    ИгорьК Давно здесь

    Если разговор пошел в эту сторону, то советую вот этот ресурс... Там вопросы "на дальность" и "на законность" проработаны очень хорошо. И в России есть производитель, который имеет очень хорошую продукцию. Надо только поискать и поискать. Или спросить Яндекс.
    Ясный пень, вещи специализированные, но если очень надо...
     
    Последнее редактирование: 20 июн 2014
    Megakoteyka нравится это.
  17. ИгорьК

    ИгорьК Давно здесь

    Теперь пробежимся по библиотеке. В целом. Самые простые команды для начала работы.
    RF24 radio(9,10); - начинаем с этого. Создаем класс radio c парой ножек (9,10 для Уно или 9, 53 для Меги) из таблицы с подключением.
    const uint64_t pipe00 = 0xE8E8F0F0E1LL; - следующее, что нужно сделать, создать константу pipe00. Pipe - это труба с адресом из пяти байтов. В "трубы" мы передаем, трубы мы слушаем. Причем труба - это не номер канала передачи. Это идентификатор принимающего/передающего устройства на рабочем канале.
    Их можно организовать еще пять. Но есть ограничение: может различаться лишь последний значащий байт. Например, вот что еще можно определить:
    const uint64_t pipe01 = 0xE8E8F0F0E2LL;
    const uint64_t pipe02 = 0xE8E8F0F0A2LL;
    const uint64_t pipe03 = 0xE8E8F0F0D1LL;
    const uint64_t pipe04 = 0xE8E8F0F0C3LL;
    const uint64_t pipe05 = 0xE8E8F0F0E7LL;

    Меняем только последний байт!
    radio.begin(); - будет следующей необходимой командой - пора начинать!
    radio.setChannel(0x22); - установим канал в котором ранее обнаруживались одни нули. Это можно и не делать, если в канале 76 (по умолчанию) тоже одни нули.

    Дальше сладкая парочка:
    radio.openWritingPipe(pipe00); - если хотим что-то передавать.
    radio.openReadingPipe(1,pipe01)
    ; где 1 - номер трубы для прослушки.
    Что еще надо знать - openWritingPipe пишет адрес трубы в НУЛЕВУЮ область труб. Если вы дадите команду openReadingPipe(0,pipe01), то адрес первой команды будет затерт. И так, с возможностью передачи, модуль может слушать только пять труб, с 1 по 5.
    radio.startListening(); - начали слушать эфир, все готово.


    Как передать что-то простое? например,
    int out = 100;
    radio.stopListening();
    - хватит слушать, готовимся передавать!
    Отправка осуществляется командой
    radio.write( &out, sizeof(out) ); Разберем. &out - это не переменная out, а ее адрес. Все переменные хранятся где-то в памяти, знак &, привязанный к имени переменной, указывает именно на это. Функции передается не значение (100), а адрес ячейки памяти и количество байт для прочтения из него: sizeof(out) - оператор вычисления длинны переменной. Для int это всегда 2. Но мы можем передавать не только переменные, но и небольшие массивы - не больше 32 байт. И там оператор sizeof просто необходим.
    radio.startListening(); - продолжаем заслушивание, если это вообще надо передатчику.

    Ну а как "словить" наше число на приемнике? Важное дополнение! Вот так:
    int in;
    radio.read( &in, sizeof(in) );

    Полный код организации приема будет таким:
    Код (C):
    int in;
    bool done = false;                            //Вспомогательная переменная;
    if ( radio.available() ){
        while (!done) {                            // Упираемся и
            done = radio.read( &in, sizeof(in) );  // по адресу переменной in функция записывает принятые данные;
        }
    }
    Теперь наша переменная (in) заполнена полученной цифрой 100. Почему нет оператора присваивания (in = ...)? Потому что функции чтения из модуля мы передали адрес переменной (&in), а функция сама записала по этому адресу полученное значение.
    Итак, это минимальный набор действий. По умолчанию, модуль работает в режиме подтверждения приема. Передатчик будет несколько раз самостоятельно, без нашего вмешательства, долбить приемник в ожидании подтверждения. Это все замечательно, но хорошо только для случая "один на один". Дальше поговорим о чуть более сложных ситуациях.
     
    Последнее редактирование: 25 ноя 2014
    Mestniy и Пушной звер нравится это.
  18. ИгорьК

    ИгорьК Давно здесь

    Примечание. Во всех приведенных автором библиотеки примерах присутствует заголовок библиотеки "printf.h".
    Чтобы не мучиться с этим файлом, исключите ссылку на него #include "printf.h", а в конце скетча добавьте две функции:
    Код (C):
    int serial_putc( char c, FILE * ) {
      Serial.write( c );
      return c;
    }

    void printf_begin(void) {
      fdevopen( &serial_putc, 0 );
    }
     
     
    MickNich нравится это.
  19. Пушной звер

    Пушной звер Оракул

    разрешено пол ватта, радиолюбители могут получить разрешение и на стационарные 20 ватт.. это вроде совсем не сложно
    та и к моей 5 ваттной рации еще не кто не придирался.

    но имхо лесть на 433 стоит только когда 2.4 не работает, скажем на пути радиоканала внезапно выросло дерево.
     
  20. ИгорьК

    ИгорьК Давно здесь

    Следующий вопрос - как работать с несколькими трубами.
    В библиотеке есть две функции: bool available (void) и bool available (uint8_t *pipe_num).
    Если первая требуется для того, чтобы знать есть ли в буфере единственной объявленной трубы что-то для чтения, то вторая позволяет работать со всеми, но для этого ее необходимо правильно вызывать, а именно:
    Код (C):
    int in;               //Переменная для приема информации;
    uint8_t pipe_num;     //Переменная, которая покажет нам из какой трубы достали информацию;
    bool done = false;    //Вспомогательная переменная;
    if ( radio.available(&pipe_num) ) { //Скармливаем функции адрес переменной, которая покажет нам номер трубы;
        while (!done) {   // Упираемся и
            done = radio.read( &in, sizeof(in) );   // по адресу переменной in функция записывает принятые данные;
        }
    }
    Постоянный вызов указанного выше кода позволяет последовательно прочитывать содержимое буферов, если там что-то есть, то есть в переменной in будет полезная информация, а в переменной pipe_num - номер трубы, из которой она поступила.

    Обратим внимание еще на некоторые полезные функции.
    setPayloadSize (uint8_t size); - функция определяет размер буфера обмена. По умолчанию размер буфера 32 байта. Если меняем это значение, то обязательно как на приемнике, так и на передатчике.
    void setRetries (uint8_t delay, uint8_t count); - функция изменяет время повтора и количество попыток передатчика "достучаться" до приемника, пример:
    setRetries (15, 15); - с задержкой 15 мс 15 попыток.
    void setPALevel (rf24_pa_dbm_e level); - устанавливает уровень мощности передатчика, и может принимать значения RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX. Пример:
    setPALevel (RF24_PA_MAX); - максимальный уровень мощности передатчика.
    bool setDataRate (rf24_datarate_e speed); - одна из трех скоростей, должна быть одинаковой на приемнике и передатчике. Принимает значения RF24_1MBPS, RF24_2MBPS, RF24_250KBPS. 250 кбит занимает более узкую полосу и является наиболее чувствительной скоростью приема. По умолчанию - 1Мбит Пример:
    setDataRate (RF24_250KBPS); ...

    Теперь передадим/примем массив.

    Передатчик:
    Код (C):
    int massive[3] = {100, 200, 300};
    radio.write( massive, sizeof(massive) );
    Приемник:
    Код (C):
    int massive[3];
    if (radio.available()) {
        bool done = false;
        while (!done) {
          done = radio.read(massive, sizeof(massive) );   //Здесь массив наполняется данными;
        }
    }
    Помним, что для массива его имя является указателем адреса. А еще помним, что модуль может протолкнуть через себя максимум 32 байта. То есть для int, массив может быть не более 16 элементов. Массив может быть любого типа, лишь бы вписаться в ограничения.
     
    Последнее редактирование: 25 июн 2014
    vlad123419 и altex нравится это.