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

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

  1. Faig

    Faig Нерд

    P.S: Я написал для себя маленькую библиотеку позволяющую передавать и принимать огромные строки текста с старт и стоп сигналами и CRC проверкой целостности всей строки. Если кому-то надо выложу на Github, и брошу ссылку здесь.
     
  2. Сделал кнопку SOS (передатчик отправляет состояние кнопки — нажата или нет, приёмник принимает и отправляет подтверждение при нажатии на кнопку) и столкнулся с такой проблемой: если приёмник и передатчик находятся рядом (в пределах 10-20 метров), всё работает корректно. Как только установил на месте (2 загородных дома, прямая видимость, расстояние между ними — метров 200). Передатчик работает стабильно, а приёмник зависает после 5 минут работы. На приёмнике я вывел светодиод, который включается, как только получил данные от передатчика. Вот этот светодиод через 5 минут начинает гореть постоянно, как будто ping = 100%, после перезагрузки работает столько же и виснет опять.
    Кто-нибудь сталкивался с подобным?

    Код передатчика:
    Код (C++):
    #include <SPI.h>
    #include "nRF24L01.h"
    #include "RF24.h"
    #include <Bounce2.h>

    #define BTN 4
    #define LED_ON 5
    #define LED_SOS 6
    #define LED_G 3

    #define BTN_PRESSED_LONG 2000 // milliseconds to reset SOS signal
    #define SEND_DELAY 100 // delay for sending data to the receiver

    RF24 radio(7, 8);
    const uint64_t pipes[2] = {0xF0F1F2F3F4LL, 0xF0F1F2F3F5LL};
    //uint32_t reply; // reply from the receiver (42)

    Bounce bouncer = Bounce();
    unsigned long pressed_moment;
    unsigned long sent_moment;  // moment of time data was sent to the receiver

    int current_mode=0;
    int confirm=0;  


    void setup()
    {
      // Setting up radio
      //Serial.begin(115200);
      radio.begin();
      delay(2000);
      radio.setDataRate(RF24_1MBPS);
      radio.setPALevel(RF24_PA_LOW);
      //radio.setPALevel(RF24_PA_HIGH);
      radio.setChannel(120);
      radio.setCRCLength(RF24_CRC_8);
      radio.setAutoAck(true);
      radio.enableAckPayload();
      radio.setRetries(15, 15);
      radio.setAddressWidth(4); // 4 bytes
      radio.openReadingPipe(1,pipes[1]);
      radio.openWritingPipe(pipes[0]);
      // Setting up pins
      pinMode(BTN,INPUT);
      pinMode(LED_ON,OUTPUT);
      pinMode(LED_SOS,OUTPUT);
      pinMode(LED_G,OUTPUT);
      bouncer .attach(BTN);
      bouncer .interval(5);
    }

    void loop()
    {
      send_data();
      Serial.flush();
    }

    void send_data()
    {
      // Checking SOS button state
      if (bouncer.update())
      {
        if (bouncer.read()==0)
        {
          pressed_moment=millis();
        }
        else
        {
          if ((millis()-pressed_moment)<BTN_PRESSED_LONG)
          {
            current_mode=1;   // 1 - send SOS signal
           
          }
          else
          {
            current_mode=0;   // 2 - reset SOS
          }
        }
      }
      // Sending data (current_mode) every SEND_DELAY ms
      /*
      if((millis()-sent_moment)==SEND_DELAY)
      {
        // Sending SOS button state
        radio.write(&current_mode,sizeof(current_mode));
        Serial.print("Current mode: ");
        Serial.println(current_mode);
        sent_moment=millis();
      }*/

      // Sending SOS button state
      radio.write(&current_mode,sizeof(current_mode));
      //Serial.print("Current mode: ");
      //Serial.println(current_mode);
      delay(100);

      if(current_mode==0) // no panic
      {
        digitalWrite(LED_ON,HIGH);
        digitalWrite(LED_SOS,LOW);
        digitalWrite(LED_G,LOW);
      }
      else if(current_mode==1)  // SOS button was pressed
      {
        digitalWrite(LED_ON,LOW);
        digitalWrite(LED_SOS,HIGH);
        digitalWrite(LED_G,LOW);
        confirmation_check();
      }
     
      else
      {
        digitalWrite(LED_ON,HIGH);
        digitalWrite(LED_SOS,LOW);
        digitalWrite(LED_G,LOW);
      }
    }

    void confirmation_check()
    {
      radio.startListening();
      if(radio.available())
      {
        radio.read(&confirm,sizeof(confirm));
        //Serial.print("Confirmation from the receiver: ");
        //Serial.println(confirm);
      }
      if(confirm==1)
      {
        digitalWrite(LED_G,HIGH);
        //Serial.println("confirmation received");
        current_mode=0;
        confirm=0;
      }
      else
      {
        digitalWrite(LED_G,LOW);
      }
      radio.stopListening();
    }
     
    Код приёмника:
    Код (C++):
    #include <SPI.h>
    #include "nRF24L01.h"
    #include "RF24.h"
    #include <Bounce2.h>

    #define LED_W 5
    #define LED_R 6
    #define LED_ON 9  // LED is on when Arduino is powered on
    #define LED_Y 10 // LED is on when Arduino is sending SOS confirmation to the transmitter
    #define BTN 4     // button to confirm SOS signal received
    #define BUZZER 3
    #define BTN_PRESSED_LONG 2000 // milliseconds to reset SOS signal
    #define RECEIVE_DELAY 3000  //delay for receiveing data from the transmitter
    #define REPLY_TIME 25000    // amount of time when receiver will send reply to the transmitter
    #define VOLUME 250   // buzzer volume
    #define BUZZ_DELAY 2000 // buzzer delay between beeps

    Bounce bouncer = Bounce();
    unsigned long pressed_moment;
    unsigned long received_moment; // not used now
    unsigned long buzz_moment;

    int current_mode=0;   // reads states from the button
    int data=0;   // received data from the transmitter

    RF24 radio(7,8);
    const uint64_t pipes[2] = {0xF0F1F2F3F4LL, 0xF0F1F2F3F5LL};
    //uint32_t reply=42;  // reply to transmitter to be sure his message was received

    void setup()
    {
      // Setting up radio
      //Serial.begin(115200);
      radio.begin();
      delay(2000);
      radio.setDataRate(RF24_1MBPS);
      radio.setPALevel(RF24_PA_LOW);
      //radio.setPALevel(RF24_PA_HIGH);
      radio.setChannel(120);
      // tested channels: 79
      radio.setCRCLength(RF24_CRC_8);
      //radio.setAutoAck(true);
      radio.enableAckPayload();
      //radio.setRetries(15, 15);
      radio.setAddressWidth(4); // 4 bytes
      radio.openReadingPipe(1,pipes[0]);
      radio.openWritingPipe(pipes[1]);
      // Setting up pins
      pinMode(LED_W,OUTPUT);
      pinMode(LED_R,OUTPUT);
      pinMode(LED_ON,OUTPUT);
      pinMode(LED_Y,OUTPUT);
      pinMode(BTN,INPUT);
      pinMode(BUZZER,OUTPUT);
      bouncer .attach(BTN);
      bouncer .interval(5);
    }

    void loop()
    {
      digitalWrite(LED_ON,HIGH);
      get_data();
      leds();
    }

    void get_data()
    {
      radio.startListening();
      if(radio.available())
        {    
            digitalWrite(LED_W,HIGH);     // transmitter ping
            radio.read(&data,sizeof(data));
            delay(100);
            //Serial.print("data: ");
            //Serial.println(data);
            //Serial.flush();
        }
        else
        {
          digitalWrite(LED_W,LOW);
          //Serial.println("-");
        }
    }

    void leds()
    {
      if(data==0) // ping
      {
        digitalWrite(LED_R,LOW);
        analogWrite(BUZZER,0);
        digitalWrite(LED_Y,LOW);
      }
      else if(data==1)  // SOS
      {
        digitalWrite(LED_R,HIGH);
        buzzer();
        confirmation();
      }
      else if(data==2) // SOS was reset
      {
        digitalWrite(LED_R,LOW);
        analogWrite(BUZZER,0);
        digitalWrite(LED_Y,LOW);
      }
      else
      {
        digitalWrite(LED_R,LOW);
        analogWrite(BUZZER,0);
        digitalWrite(LED_Y,LOW);
      }
     
    }

    void buzzer()
    {
      if (current_mode!=1)  // If button was not pressed
      {
        if ((millis()-buzz_moment)>=BUZZ_DELAY)
        {
          analogWrite(BUZZER,VOLUME);
          buzz_moment=millis();
          delay(200);
        }
        else
          analogWrite(BUZZER,0);
      }
    }

    void confirmation()
    {
      if (bouncer.update())
      {
        if (bouncer.read()==0)
        {
          pressed_moment=millis();
        }
        else
        {
          if ((millis()-pressed_moment)<BTN_PRESSED_LONG)
          {
            current_mode=1;   // button was pressed shortly to confirm SOS received
          }
          else
            current_mode=0;
        }
      }

      if(current_mode==1)
      {
        while((millis()-pressed_moment)<REPLY_TIME)
        {
          analogWrite(BUZZER,0);
          radio.stopListening();
          radio.write(&current_mode,sizeof(current_mode));
          //Serial.println("SOS confirmation was sent");
          delay(100);
          digitalWrite(LED_Y,HIGH);
        }
        current_mode=0;
        get_data();
      }
      else
      {
        digitalWrite(LED_Y,LOW);
      }
    }
     
     
  3. Faig

    Faig Нерд

    У меня похожая проблема. С этой же проблемой я хотел бороться мониторингом статуса модуля и перезагружать его если модуль завис.
     
  4. Т.е. скорее всего проблема железная либо в arduino, либо в модуле?
    У меня китайские аналоги arduino nano из Чип и Дипа и модуль nRF24L01+ из Амперкота. У вас похожая ситуация? Я пока не пробовал заменять компоненты, т.к. был не готов к такому повороту и не взял с собой на локацию замену.
    Кстати, как вы планируете мониторить arduino? С помощью другого arduino и реле?
     
  5. Faig

    Faig Нерд

    На солько я смог выяснить, сам ардуино работает нормально, так как даже его перезагрузка не исправляет ситуацию (как уже заметил, радиомодули и ардуино запитаны из разных источников). Так что, выходит , зависает сам радиомодуль.
    У меня к вам вопрос. Если ваши модули зависают строго по истечению 5 минут, может у вас каждые 5 минут выполняется какая-то подпрограмма(функция) и она и вызывает зависание? (поленился изучить ваш код:) )
    Попробуйте вместо "RF24_PA_LOW" установить "RF24_PA_HIGH" или "RF24_PA_MAX", так как у вас приличное расстояние между устройствами.

    У меня радиомодули не столь пунктуальны...) А потому прошу вас сравнить результаты данной команды при норм. работе и при сбое:
    Код (C++):

    // вот код который надо добавить до вызова нужной функции если его у вас еще нет
    #include <printf.h>
        setup(){
         printf_begin();
         Serial.begin(ваша скорость);
         //...
        }


    //вызвать в любом месте(можно и по нажатии кнопки или каждые несколько секунд)
      radio.printDetails();
    Я покопался в библиотеке RF24.h и нарыл прямые команды радиомодулю и на одну из них основная надежда. Если не трудно попробуйте и ее.
    Код (C++):
    //это функция обьявить до setup()
    uint8_t spiTrans(uint8_t cmd) {

        uint8_t status;

        radio.beginTransaction();
        status = _SPI.transfer(cmd);
        radio.endTransaction();

        return status;
    }

    //это функция обьявить до setup()

    uint8_t get_status(void) //возвращает "0" если модуль выключен, "14" если модуль включен и режиме listening
    {
        return spiTrans(NOP);
    }


    //вызов функции, опять же или по нажатию или каждые неск. секунд
        Serial.print("module status: ");
        Serial.println(spiTrans(NOP)); //"NOP" is "get status" instruction
    Update: Я немного ошибся включен ли "startListening" можно увидеть вызвав это
    Код (C++):
    radio.print_byte_register(PSTR("CONFIG\t"), NRF_CONFIG);
    Listening on - 0x0d
    Listening off - 0x0c

    A "getstatus" нужно использоватькак аргумент для этого метода
    Код (C++):
    radio.print_status(get_status()); //помоему печатает строку состояния регистрок
    Чтобы были доступны защищенные методы, я добавил в самом начале класса библиотеки "public", а "private" и "protected" закоментировал

    P.S: Использую 4 разных модуля, купленных у разных продавцов. Имеются и дальнобойные с антеннами и обычные без антенн.
     
    Последнее редактирование: 4 янв 2018
  6. Faig

    Faig Нерд

    Победим эти модули безоговорочно!!

    Вот список прямых команд для радиомодуля, нарытых в библиотеке RF24.h
    Код (C++):
    /* Instructions code*/
    #define R_REGISTER       0x00
    #define W_REGISTER       0x20
    #define REGISTER_MASK    0x1F
    #define ACTIVATE         0x50
    #define R_RX_PL_WID      0x60
    #define R_RX_PAYLOAD     0x61
    #define W_TX_PAYLOAD     0xA0
    #define W_ACK_PAYLOAD    0xA8
    #define FLUSH_TX         0xE1 //очистить буфер отправки
    #define FLUSH_RX         0xE2 //очистить буфер приема
    #define REUSE_TX_PL      0xE3 //(кажись) отправить то что в буфере повторно
    #define NOP              0xFF //показать статус устройства (0-выкл; 14 - кажется, слушает)
    Отправляются вот этой функцией
    Код (C++):
    //sends some command to SPI device (in our case, radio module)
    uint8_t spiTrans(uint8_t cmd) {

        uint8_t status;

        radio.beginTransaction();
        status = _SPI.transfer(cmd);
        radio.endTransaction();

        return status;
    }
    Команду надо передать в функцию , при ее вызове, как аргумент.
     
    Последнее редактирование: 1 янв 2018
  7. Нет, в коде просто прописан постоянный запрос данных от приёмника и включение светодиода и баззера в зависимости от полученных данных. Данные запрашиваются с интервалом 1/10 секунды. Никакого действия по истечении 5 минут не предусмотрено. И про 5 минут я выразился условно, это может быть как 3 минуты, так и 10 )
    Опять же, если приёмник и передатчик находятся вблизи — в пределах квартиры, например, где я и отлаживал работу изначально — всё работало стабильно на протяжении 10-12 часов (выделить больше времени на тест не было возможности).

    Спасибо, обязательно испробую предложенные вами варианты и отпишу, но только после 20 января (я уехал в отпуск от ардуино :)
     
  8. Kislotik

    Kislotik Нуб

    Очень прошу помощи, есть пульт на ардуино и nrf24l01, и приемник на ардуино и nrf24l01, делаю прикормочный кораблик. Столкнулся с проблеммой если на пульте во время движения кораблика сели батарейки и сигнал пропал, то кораблик будет плыть пока не сядет аккумулятор, подскажите что должно быть написано в скетче в приемнике и передатчике для того что бы при пропадании сигнала мотор выключался. Мне не обязательно точные строчки(хотелось бы =)) ), хотябы примерно. Извините если не туда написал.
     
  9. Faig

    Faig Нерд

    Вы не туда написали, это уже логика микроконтроллера управляющего моторчиком и ей не важно какой приемник принимает сигнал.

    P.S: считаю такие техно-приблуды нечестной рыбалкой, да и вообще любительскую рыбалку считаю очень жестокой (кроме подводной).
     
  10. Kislotik

    Kislotik Нуб

    Вы бы подсказали по теме. А не навязывали свою точку зрения.
     
  11. issaom

    issaom Гуру

    Не знаю насколько будет по теме так как с nrf24l01 сам не работал… Общий алгоритм реализации простой: приемник (в данном случае ваш кораблик) должен опрашивать передатчик с определенным интервалом (запрашивать управляющие команды состояние кнопок, джойстиков, резюков или чем вы там им управляете) и если ответ не получен давать команду на остановку моторов (если есть гироскоп можно кораблик на 180 развернуть). На uart-модулях такая схема программно реализуется достаточно легко и актуальна если нужно получать телеметрию с управляемого девайса. Более простой вариант слать команды с передатчика через определенный временной интервал - кораблик должен будет остановиться если не получил ни одну команду через определенный промежуток времени.
     
    Андрей Ипатов, sys и Kislotik нравится это.
  12. Kislotik

    Kislotik Нуб

     
  13. Kislotik

    Kislotik Нуб

    Огромное спасибо, буду что то выдумывать)
     
  14. sys

    sys Злобный Буратино Модератор

    как вариант: можно еще ик-передатчик на берегу поставить, а ик-приемник на носу корабля. и как сигнал безнадежно пропал - разворачивать корабль, пока не поймает нужный ик-сигнал и тогда направить его к берегу. этакий маяк
     
  15. Kislotik

    Kislotik Нуб

    У меня пока не получилоси реализовать действие при потерисвязи, но идея с ик очень даже прикольная))
     
  16. ORLENOK

    ORLENOK Гик

    Всем привет.
    Используют библиотеку RF24, наверное, как и многие тут. Скажите, как в методе writeAckPayload передать массив float? Сейчас использую такой код:
    Код (C++):
    float sendingData[] = {1.23,2.34};
    ...
    radio.writeAckPayload(1,sendingData, sizeof(sendingData) );
    Не работает. На запрашиваемой стороне не видит подтверждения Ack. Якобы этот метод не срабатывает.
    т.е. вот этот метод:
    Код (C++):
    radio.write(&counter,sizeof(counter))
    Начинает возвращать только false.
    Но если пересылать массив из одного элемента, то всё ок.
    Вообще идея такая, что одна ардуина периодически опрашивает другие, те в ответ пересылаю массивы данных. Может быть вы как-то иначе это реализуете?
     
  17. Доброго времени суток.
    Вопрос: кто-нибудь практически пробовал к одной ардуине (напиример уно) подключить nRF24L01
    в качестве приемника и к этой же ардуине четырёхразрядный индикатор (Troyka-модуль) для вывода полученных из радиомодуля данных? И что из этого вышло?
    У меня индикатор не заработал.
     
  18. Faig

    Faig Нерд

    Сперва о глюках:
    1) Замечу что "acknowledge"(автоответ) может не приниматься или не отправляться просто изза глюкнувшего модуля, так что сперва выключите питание(физически) и включите его заново и на приемнике и на передатчике.
    2) В некоторых моделях (как заметил автор темы) выключение автоответа наоборот включает его "setAutoAck(true);" или "setAutoAck(false);" . Попробуйте их по очереди.

    О вашем коде:
    Ну а потом там есть ограничение на размер автоответа, и уж точно не принимаются "float"-ы. Судя по словам автора темы максимальный размер автоответа - "uint32_t" (4 байта)
     
  19. Faig

    Faig Нерд

    По какой шине он подключается можно конкретнее? SPI? Если да то вам придется почитать как управлять одновременно подключенными устройствами на одну шину.
    Если нет, то я подключал много чего к ардуине и они нормально сосуществовали , при условии если тока не жрут много, или если ваш NRF24 питается с другого места (в таком случае не забудьте объединить земли доп. питания и вашей ардуины).
     
  20. ORLENOK

    ORLENOK Гик

    Автоответ вообще работает. Он не передает массив из 2х float. Если передавать один, то всё ок. С byte тоже всё ок.

    float принимает и только один, видимо. Он как раз 4 байта.
    А где можно почитать про ограничения? Это сделано в библиотеке или это заложено на уровне железа?