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

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

  1. ИгорьК

    ИгорьК Гуру

    В этой теме уже не раз говорилось, что с программной точки зрения в сетапах обоих ардуин нужно выставить полный комплект возможных определений. Из Вашего скетча это не следует.
    Приводимые у меня краткие куски кода лишь для понимания что к чему. Но практика показала, что инициализировать пару модулей нужно абсолютно по всем параметрам.
    И про конденсатор, про конденсатор... Слишком у многих людей без него ничего не получалось. Если нет конденсатора, вероятность успеха слишком мала. По крайней мере можно долго лопатить код и ничего не добиться.
     
    Последнее редактирование: 9 окт 2014
    Petrovich нравится это.
  2. Petrovich

    Petrovich Нерд

    Спасибо! Вот на Channel(0) именно и заработало,что натолкнуло на то, что я каналы не правильно задавал. Но вот почему на канале 76(поумолчанию который) не работало, если канал явно не указывать...Ну да ладно, будем считать что setChannel - обязательно.
     
  3. Alexium

    Alexium Нуб

    Здравствуйте еще раз. Скажите, целесообразно ли, используя стандартные библиотеки для этого модуля, делать в программе проверку на успешность передачи данных? Мол, если пришла "1", отошли "ок" и если отправил "1", но ответа нет, повтори. Может быть это уже вшито в, так называемый, протокол?
     
  4. ИгорьК

    ИгорьК Гуру

    Это вшито в протокол комбинацией следующих установок:
    radio.setRetries(int, int); - интервал и количество попыток достучаться до приемника;
    radio.setAutoAck(1); - соответственно, подтверждение приемником самого приема.
    radio.write( &out, sizeof(out) ); возвращает bool значение true, если "Message is successfully acknowledged by the receiver or the timeout/retransmit maxima are reached." То есть если сообщение успешно опознано приемником или исчерпано количество попыток его отправить.
    Таким образом, ИМХО, практическая ценность функции на подтверждение приема слабовата.
    В своем проекте я делаю проверку получения "в лоб": приемник получил команду - отправил передатчику подтверждение. И так до тех пр, пока все это не совпадет. Работу можно посмотреть здесь. Там заметно время между нажатием кнопки и изменением режима. Это время складывается не только запаздыванием интернета и паузами взаимодействия, но в том числе и тем, что программно через 0.5 секунды, если подтверждение не пришло, передатчик повторяет команду.
     
    Последнее редактирование: 30 окт 2014
    Alexium нравится это.
  5. ANV

    ANV Гуру

    Игорь, а поясните, пожалуйста про трубы, из даташита не совсем понял как работать с модулями если они не нужны.
    Что надо от модулей:
    - есть центральный модуль и периферийные
    - надо чтобы передачу мог инициировать как центральный, так и периферийный модули
    - передача данных только между центральным и периферийными модулями (периферийные напрямую не общаются)
    - количество периферийных модулей больше 6
    Можно ли это реализовать?
     
  6. ИгорьК

    ИгорьК Гуру

    Конечно можно. Если вы работаете и на передачу и на прием, то вы уже можете слушать не шесть, пять труб.

    Что это значит? Что пять модулей могут одновременно вам что-то слать, и не произойдет взаимного умопомешательства: вы будете последовательно читать информацию из каждой трубы, а железо само не допустит их пересечения.

    Но если модулей больше - надо решать вопрос с помехами, возникающими при попытке одновременной передачи двумя и более модулями в одной трубе. То есть можно слушать и одну трубу, в которую вещают 20 модулей, лишь бы они не делали это одновременно.
    Как это решать. (Честно говоря, у меня этой ситуации не было, так что лишь мысли).
    Конечно, теперь уже придется действовать и программным, а не только железным путем.

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

    То есть:
    1. В одной трубе у двух передатчиков иницализация radio.setRetries(... , ...); должна быть разная в части количества попыток раза в два, например 7 и 15. Если так случилось, что начали вещать оба и ничего не получается - должен остаться лишь один.

    2. Приемник, уже программно, должен подтвердить прием,а передатчик - программно жать подтверждения. Иначе начинать передачу заново. Время такого ожидания/передачи у пары, работающей в одной трубе, тоже должно не совпадать.

    В принципе, все "железное" (булево) подтверждение передачи функции библиотеки не стоит выеденного яйца, поскольку возвращает единицу и в случае успешной передачи, и в случае исчерпания попыток передачи. Так что если это не передача температуры ради развлечения, а что-то более важное, то и при одном передатчике/приемнике неплохо бы программно убеждаться в успехе деятельности.
    А уж из этого следует, что надо писать код с учетом помех, вызываемых как несколькими передатчиками в одной трубе, так и другими помехами.
    Как-то так.

    Или использовать библиотеку, которая создает сеть из модулей.

    UPD. Подумав. Если один передатчик а приемников больше пяти, то придется у всех отключать железное подтверждение приема. Действовать программно. Передатчик прокричал в трубу нечто, с идентификтором приемника. Все приняли, но обратно подтвердил только тот, чей идентификатор совпал. Остальные - проигнорировали.
    Если целевой приемник не услышал - передатчик все повторяет.
    В общем, надо экспериментировать.
     
    Последнее редактирование: 18 ноя 2014
    MickNich нравится это.
  7. magic21

    magic21 Нерд

    Провел с этой темой в "обнимку" пару вечеров=) Хотел сказать большое спасибо Игорю.
    В общем я провёл такой эксперимент, правда пробовал с одним передатчиком и двумя приёмниками... но всё работает... в одну трубу посылаем запрос в ней же слушаем ответ... получают оба, отвечает только тот кому отправил... "На столе" всё работает как часы, с первой отправки... правда попробовал сменить модуль nrf24l01+ (все три были из одной партии, конденсаторы не припаивал) на модуль с усилителем и антенной... приёмники получают, но ответ перестал приходить... нужно пробовать реализовать повторы...

    Так же была проблема как и у k0rwin, "лечил" так же как и он:
    Код (Text):
    radio.startListening();

    radio.stopListening();
    Но прочитав здесь https://arduino.ru/forum/programmirovanie/nrf24l01?page=11#comment-87660 перешёл на другую библиотеку... https://github.com/tmrh20/RF24 и всё заработало без лекарства.
    Библиотека форк от оригинальной написанной автором Maniacbug, и требуется немного изменить код, в частности функция read перестала быть bool.

    Хотел попробовать с шестью приёмниками, но передумал) т.к. если работает с двумя будет наверно работать и с шестью. Еще у tmrh20 есть интересная библиотека RF24Mesh, правда написано, что в стадии концепта... но всё равно хочу попробовать как будет работать.

    Думаю есть смысл всем пробовать с библиотекой от tmrh20, т.к. автор живой и постоянно вносит правки, а Maniacbug забросил это дело.
     
    AlexVS и ИгорьК нравится это.
  8. AlexVS

    AlexVS Гик

    Заменил библиотеки, перекомпилировал скетчи для всех устройств, мне кажется, что связь между узлами стала более надежная. Так что спасибо за наводку.
    Дополнительно скачал DigitalIO, и заменил библиотеки iBoard на RF24.h + DigitalIO.h
     
    magic21 нравится это.
  9. ИгорьК

    ИгорьК Гуру

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

    // Стало:
    if( radio.available()){
       radio.read(&in,sizeof(in));             // по адресу переменной in функция записывает принятые данные;
    }
     
    В остальном, вроде, изменения в коде не требуется. То есть (пока) все что написано ранее на тему - остается в силе.
    А вот новые вкусные функции в этой библиотеке обнаруживаются.
     
    Последнее редактирование: 25 ноя 2014
    magic21 нравится это.
  10. Уважаемые борцы, вопрос по модулю !!! Я целый день занимаюсь с ним любовью. Если у меня выставлен второй канал

    radio.setChannel(2); // сканером по нулям, самый чистый

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

    Код (Text):
    //Передатчик

    #include <SPI.h>
    #include <nRF24L01.h>
    #include <RF24.h>

    const uint64_t writingPipe = 0xE8E8F0F0AALL;

    int out = 99;

    RF24 radio(9, 10);

    void setup() {
        Serial.begin(57600);
        delay(2000);                    
        radio.begin();                    
        radio.setChannel(2);                
        radio.setRetries(15,15);            
        radio.setDataRate(RF24_250KBPS);  
        radio.setPALevel(RF24_PA_MAX);    
        radio.setAutoAck(1);              
        radio.openWritingPipe(writingPipe);
    }

    void loop()
    {
    radio.write(&out,sizeof(out));
    Serial.println(out);
    delay(1000);

    }


    //Приемник
    #include <SPI.h>
    #include <nRF24L01.h>
    #include <RF24.h>

    const uint64_t readingPipe = 0xE8E8F0F0AALL;

    int in;

    RF24 radio(9, 10);

    void setup() {


        delay(2000);                
        Serial.begin(57600);
        radio.begin();                  
        radio.setChannel(2);              
        radio.setRetries(15,15);          
        radio.setDataRate(RF24_250KBPS);  
        radio.setPALevel(RF24_PA_MAX);    
        radio.setAutoAck(1);              
     
        radio.openReadingPipe(1,readingPipe);
        radio.startListening();            
    }

    void loop()
    {

      if( radio.available()){
      radio.read(&in,sizeof(in));
        Serial.println(in);
        delay(1000);
    }

    }


     
    Посмотрите плиз код, все ли правильно я понял по модулю....
     
  11. ИгорьК

    ИгорьК Гуру

    Навскидку все верно. Задержку в приемнике поставьте эдак не больше 200. Библиотеку берите из последних постов, как показавшую себя наиболее толковой. Ну и, добро пожаловать...
    А каналы менять не спешите. На малых каналах должно работать в любой обстановке. По крайней мере на небольшой дистанции.
     
  12. Добрый вечер победители! Нам удалось победим модули. Единственное не удалось добиться обратной связь от приемника к передатчику.

    Посмотрите плиз код, может где ошибка зарыта.

    Приемник:

    Код (Text):
     if(radio.available())
        {
          radio.read(&in,sizeof(in));
         
          if (in==11)
            {
                radio.stopListening();
                Serial.println("Receive command - Start");
                message = 11;
                radio.writeAckPayload(1, &message, sizeof(message));
                digitalWrite(7,HIGH);
                radio.startListening();
               
            }
          if (in==10)
            {
                radio.stopListening();
                Serial.println("Receive command - Stop");
                message = 10;
                radio.writeAckPayload(1, &message, sizeof(message));
                digitalWrite(7,LOW);
                radio.startListening();
            }
     
          delay(200);
        }
    Передатчик
    Код (Text):
     if (radio.isAckPayloadAvailable() )
              {
                radio.startListening();
                radio.read(&message,sizeof(message));  
                Serial.println(message);
                radio.stopListening();
               
              }
    Передатчик шлет комманду на включение светодиода на ножке 7. (11 - Включить, 10 - Выключить)

    После получения комманды приемник должен отключиться от прослушки и сделать комманду write, затем ключить прослушку

    message объявлена как static unit_32t. Сетапы обоих приведены ниже

    Код (Text):
    Serial.begin(57600);
        Serial.print("Waiting to start NRF Coordinator");
        delay(2000);
        radio.begin();                  
        radio.setChannel(2);              
        radio.setRetries(15,15);          
        radio.setDataRate(RF24_250KBPS);  
        radio.setPALevel(RF24_PA_MAX);  
        radio.enableAckPayload();            
        radio.openWritingPipe(writingPipe);
        radio.startListening();
        Serial.println(" - OK");  
    Код (Text):
    Serial.begin(57600);
        Serial.print("Waiting to start NRF Router X1");
        delay(2000);
        radio.begin();                  
        radio.setChannel(2);              
        radio.setRetries(15,15);          
        radio.setDataRate(RF24_250KBPS);  
        radio.setPALevel(RF24_PA_MAX);  
        radio.enableAckPayload();
        radio.openReadingPipe(1,readingPipe);
        radio.startListening();
        Serial.println(" - OK");  
     
  13. magic21

    magic21 Нерд

    Осмелюсь предположить, что отправка автоматического ответа происходит здесь:
    Код (Text):
    if(radio.available())
    Сам с этой функцией не пробовал, но судя по коду Игоря на второй странице, скорей всего авто ответ должен быть задан заранее. Если ошибаюсь, прошу понять и простить=)

    P.S. А зачем приёмнику отвечать тем же сообщением, что он получил от передатчика?
    Ответить любым сообщением, подтвердив приём не достаточно?
     
    Последнее редактирование: 30 ноя 2014
  14. ИгорьК

    ИгорьК Гуру

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

    Эта функция полезна может быть вот когда. Приемник, например, постоянно измеряет какой-то параметр и кладет его в загашник AckPayload для отправки. Отправка происходит в ответ на какое-то сообщение как раз с целью вызвать этот ответ. Например есть пять датчиков температуры, висят таким образом в пяти местах, а передатчик, когда ему нужно, опрашивает последовательно приемники ничего не значащими сообщениями, и получает информацию через AckPayload подтверждение.
     
  15. Отвечать нужно тем же сообщением, так как передатчик должен знать, что в этот момент делает первый приемник.


    Мы подключили 4 роутера и 1 координатора и у всех одна и таже труба. Отсылаем команды на приемники таким способом.

    Передатчик всем: "1001"

    Ответ придет только от первого приемника, так как он обрабатывает команды начинающие с 1.

    Передатчик всем: "2001"

    Ответ придет от второго приемника, так как он обрабатывает команды начинающие с 2.

    И не надо использовать "6 труб"


    Проблему с ответами решили другим способом:

    с AckPayload не получилось, скажу сразу. Сделали через startListening и stopListening

    Код (Text):
    if (CommandValue=="StopX1")
                    {
                      out=10;
                   
                      radio.stopListening();
                      radio.write(&out,sizeof(out));
                      Serial.println("Stop X1 - (10)");
                      radio.openReadingPipe(1,readingPipe);
                      radio.startListening();
                    }
    и в лупе слушаем каждое сообщение и выводим в serial, а serial читаем на мониторе на компе, так как дисплей подсоединять на пульт нету времени, а ком порт слушается прогой с любым интерфейсом.

    Код (Text):
    if(radio.available())
                {
                  radio.read(&message,sizeof(message));
                  Serial.println(message);
                }
    Но есть проблемы с двухсторонней связью
    У кого есть идеи ????

    Мы посылаем команду приемнику 1001 - включить мотор. Приемник понял, что поступила команда 1001 включить мотор и начала его включать, а вот обратно передатчику команду по какой то причине не отослала. Координатор смотрит, что ничего не пришло и стопорит мотор, отсылая команду 1002 - остановить мотор.

    "не включаю противотуманки когда нет тумана" ИгорьК правильно заметил.

    Я думаю, что AckPayload сделали специально для таких целей. Но вот заставить ее работать так и не получилось.
     
  16. Да хочу сразу сказать, что датчики работают без кондера.
     
  17. magic21

    magic21 Нерд

    Я пробовал и без AckPayload. в одной трубе отправлял и слушал.
    Отправляем 101 получают все, отвечает только "стопервый" ответом 1001.
    ну и т.д.
    "На столе" всё работает. Нужно дорабатывать, реализовывать повторы, т.к. при удалении бывает ответ не доходит и при смене модулей на модули с антенной, тоже что то барахлило даже на столе... всё было без кондёров.
    Выложить вам код? Может, что наковыряете для себя?
     
  18. magic21

    magic21 Нерд

    Кстати, у меня почти так же как и у вас, только наверно вы забыли, после того как перестали слушать, переключить трубу на отправку:
    Код (Text):
    if (CommandValue=="StopX1")
                    {
                      out=10;
                 
                      radio.stopListening();
                      radio.write(&out,sizeof(out));
                      Serial.println("Stop X1 - (10)");
                      radio.openReadingPipe(1,readingPipe);
                      radio.startListening();
                    }
    У меня так:
    Код (Text):
    if (dataIn==nodeNo) {//команда нам отправляем ответ
          radio.stopListening();  //Перестаем слушать
          radio.openWritingPipe(pipe); //Открывем трубу для отправки
          radio.write( &nodeRes, sizeof(nodeRes) ); // Отправляем ответ
          radio.openReadingPipe(1,pipe); //Открываем трубу для приёма
          radio.startListening();//Слушаем

        }
    И еще, в сетапе у меня:
    Код (Text):
    radio.setAutoAck(false);
     
    Последнее редактирование: 30 ноя 2014
  19. Да, мы так и сделали. Просто переставляем трубы Отправка - Получение
     
  20. magic21

    magic21 Нерд

    Ещё я жду, ответ. И кстати не используйте delay.
    Код (Text):
    void sendNode(int nodeNo/*Индификатор узла*/, int nodeRes/*Какое подтверждение ждём от узла*/)
    {
      Serial.print("Send node No:");
      Serial.println(nodeNo);
      radio.write( &nodeNo, sizeof(nodeNo) ); // Отправляем команду ответить узлу nodeNo

        //слушаем ответ
      radio.openReadingPipe(1,pipe);
      radio.startListening();
      unsigned long cMillis = millis();
      while(true){

        // проверяем есть ли ответ, если есть сравниваем от нашего узла или нет
        if ( radio.available() ) {
          int dataIn;
          radio.read( &dataIn, sizeof(dataIn));
          if (dataIn==nodeRes) {// если от нашего пишем "Есть ответ, всё хорошо"
            Serial.print("There is an answer. Ok.-");
            Serial.println(dataIn);
            break;
          }
          else {// не от нашего, пишем "Есть ответ, ошибка!"
            Serial.print("There is an answer. Error.-");
            Serial.println(dataIn);
            break;
          }
        }


        if(millis() - cMillis > 15) {//проверяем не прошли ли 15 милисекунд на ожидание ответа, если прошели то пишем нет ответа и выходим
          Serial.println("Time has expired. No answer.");
          break;
        }
      }
      radio.stopListening();
      radio.openWritingPipe(pipe);
     }