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

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

  1. Cadas

    Cadas Нерд

    Гайвер есть на Гитхабе, его скетчи - среди примеров в Arduino IDE для любых плат, а у меня нет привычки сомневаться в кодах с Хитхаба.
    Счастье уже нашел. В ближайшее время поделюсь своим счастьем.
     
  2. Un_ka

    Un_ka Гуру

    А у меня есть.
     
  3. parovoZZ

    parovoZZ Гуру

    Я код с гитхаба тоже иногда дёргаю, но всегда сверяю его с другими источниками - с даташитом, статьями и пр.
     
  4. vvr

    vvr Инженерище

    он шо, блинк изобрёл ?
     
  5. parovoZZ

    parovoZZ Гуру

    Ну, да. Все моргают LED0, а он поморгал LED1. В гору идёт!
     
  6. Kot26ru

    Kot26ru Гик

    Приветствую! Помогите разобраться почему не работает радиосвязь между нрф24 (которые с антеннами). Подключены к ардуино нано. Питание модулей нрф24 пробовал и от самих ардуино и от стороннего 3,3В. Конденсаторы по 100мкФ по питанию модулей повесил. Связи нет. Код прилагаю
    Код (C++):
    #include <SPI.h>

    #include <nRF24L01.h>
    #include <printf.h>
    #include <RF24.h>
    #include <RF24_config.h>

    // ПЕРЕДАТЧИК

    RF24 radio(10,9);                                                                     // CE CSN
    int Lat[1] = {100};                      

    void setup()
    {
         Serial.begin(9600);                                // Объявляем работу с последоватлеьным портом
         delay(250);                                        // задержка 250мс
         radio.begin();                                     // запуск радиомодуля
         radio.setChannel(85);                              // установка канала (частота передачи/приема)
         radio.setDataRate (RF24_1MBPS);                    // скорость обмена
         radio.setPALevel(RF24_PA_HIGH);                     // мощность модуля
         radio.openWritingPipe(0xE8E8F0F0E1LL);            // труба передатчика
     
         if (!radio.begin())                                // если радиомодуль не запустился
         {
            Serial.println(F("Радиомодуль не отвечает"));
            while (1) {}                                    // ждем когда запустится радиомодуль
         }
         radio.stopListening();                             // включаем режим передачи
    }
       

    void loop()
    {
        if (radio.available())                        // если связь установлена
        {
          radio.write(&Lat, sizeof(Lat));             // передаем широту  
          Serial.println(Lat[0]);
        }
        else
        Serial.println(F("Передачи нет!"));
        delay (1000);
    }
     
  7. Kot26ru

    Kot26ru Гик

    и код приемника
    Код (C++):
    #include <SPI.h>

    #include <nRF24L01.h>
    #include <printf.h>
    #include <RF24.h>
    #include <RF24_config.h>

    #include <Wire.h>
    #include <LiquidCrystal_I2C.h>

    // ПРИЕМНИК

    RF24 radio(10,9);                                                                     // CE CSN
    LiquidCrystal_I2C lcd(0x27,16,2);                                                     // Устанавливаем дисплей
    int Lat[1];

    void setup()
    {
      lcd.init();
      lcd.clear();                  
      lcd.backlight();                                                                    // Включаем подсветку дисплея
       
      Serial.begin(9600);                                // Объявляем работу с последоватлеьным портом
         delay(250);                                        // задержка 250мс
         radio.begin();                                     // запуск радиомодуля
         radio.setChannel(85);                              // установка канала (частота передачи/приема)
         radio.setDataRate (RF24_1MBPS);                    // скорость обмена
         radio.setPALevel(RF24_PA_HIGH);                     // мощность модуля
         radio.openReadingPipe(1, 0xE8E8F0F0E1LL);         // труба приемника
       
         if (!radio.begin())                                // если радиомодуль не запустился
         {
            Serial.println(F("Радиомодуль не отвечает"));
            while (1) {}                                    // ждем когда запустится радиомодуль
         }
         radio.startListening();                             // включаем режим приема
    }

    void loop()
    {
      if (radio.available())                        // если связь установлена
        {
          lcd.print("RADIO ON");
          radio.read(&Lat, sizeof(Lat));                // принимаем широту  
         
      lcd.setCursor(0, 1);                              // Устанавливаем курсор на вторую строку и нулевой символ.
      lcd.print(Lat[0]);
      Serial.println(Lat[0]);
        }
        else
        {
        lcd.print("RADIO OFF");
        delay (500);
        lcd.clear();
        delay (500);
        }
    }
     
  8. parovoZZ

    parovoZZ Гуру

    Ну, для начала необходимо один модуль перевести в режим сканера, а другим лупить в эфир данными. Частоту выбрать свободную от помех.
     
  9. b707

    b707 Гуру

    Kot26ru
    зачем у вас в передатчике условие
    if (radio.available()) ???

    Это передатчик, Вы в нем вообще из трубы ничего не читаете. Значит radio.available() никогда не будет истинно и код работать не будет
     
  10. Kot26ru

    Kot26ru Гик

    Спасибо, поправил.
     
  11. Kot26ru

    Kot26ru Гик

    В общем и целом заработала передача. Имеются два массива (координаты GPS) широта и долгота различных городов. Есть большое желание передать эти массивы с одного модуля на другой. Так как числа там с запятой, то тип массивов выбрал как float. За одну передачу передается 8 чисел (4х8=32 байта). А что делать, если чисел в массиве, допустим, 20? И таких массива два (широта и долгота). Подскажите как правильно сформировать пакеты на передачу. Желательно с контролем данных после приема. Читал ветку в попытке найти информацию об этом, но информации очень много, трудно разобраться.
     
  12. Kot26ru

    Kot26ru Гик

    Спасибо, сменил частоту, подправил код по совету ниже, поменял конденсаторы на 10 мкФ - модули работают
     
  13. parovoZZ

    parovoZZ Гуру

    А может сто чисел?
    В nRF24 три буфера на отправку и три буфера на приём. Каждый по 32 бита. В передатчике толкаешь туда пакеты по 32 бита и читаешь байт состояния буфера. Как только получил информацию о том, что буфер заполнен, останавливаешь заполнение и ждёшь его освобождения.
    Для отправки пакетов необходимо дёрнуть ногу CE и ждать прерывание по успешной доставке получателю. Затем опять дёргаешь ногу CE. И так до тех пор, пока есть что передавать.
     
  14. parovoZZ

    parovoZZ Гуру

    Я ставлю керамику на 1 мкФ. Электролиты там не помощники.
     
  15. Kot26ru

    Kot26ru Гик

    Алгоритм понятен, но что делать, неясно. С регистрами я совсем никак. Учусь только. Читал посты в теме по работе с регистрами - сложно
     
  16. parovoZZ

    parovoZZ Гуру

    Про регистры надо знать только одно - это просто переменные.
    Всё очень просто:
    Код (C++):
        //... Функция отправки данных
    void nRF_Transmit(uint8_t *tx_data)
    {
        nRF_GO();                                                            // Инициируем передачу

        SPI_WriteArray(nRF_W_TX_PAYLOAD, nRF_SEND_LEN, tx_data);            // Загрузить данные в буфер
    }
    Всё, данные улетели в космос.
    Получили инфу о том, что данные получены (к нам вернулся ACK пакет), ногу CE отпустили:
    Код (C++):
    nRF_STOP();
    Макросы:
    Код (C++):
    #define nRF_GO()                SetBit(nRF_CE_PORT, nRF_CE)
    #define nRF_STOP()                ClearBit(nRF_CE_PORT, nRF_CE)
    Вызывают ещё одни макросы:
    Код (C++):
    #define ClearBit(reg, bit)       reg &= (~(1<<(bit)))
    //пример: ClearBit(PORTB, 1); //сбросить 1-й бит PORTB

    #define SetBit(reg, bit)          reg |= (1<<(bit))  
    //пример: SetBit(PORTB, 3); //установить 3-й бит PORTB
     
  17. b707

    b707 Гуру

    передавать по частям. очевидно
     
  18. Kot26ru

    Kot26ru Гик

    мысль была в том, чтобы передавать поочередно координаты одного города, затем ждать ответа от приемника, что данные получены. Затем отправлять следующие. Но в коде явно накосячил, потому как ответ (message) от приемника не приходит. Соответственно, передатчик в вечном цикле отправки координат элемента [0] и ожидании ответа от приемника. Вроде всё по примерам делал, не пойму что не так. Помогите пожалуйста найти ошибки
     
    Последнее редактирование: 6 мар 2021
  19. Kot26ru

    Kot26ru Гик

    Добился приема. Приемник наконец то стал принимать координаты. Но ответ-подтверждение так и не отправляет. Из-за чего на приемной стороне весь массив элементов заполняется координатами только 0-го элемента.
    Код (C++):
    #include <SPI.h>;               // Библиотека для работы с шиной SPI

    #include <nRF24L01.h>;          // Файл конфигурации для библиотеки RF24

    #include <RF24.h>;              // Библиотека для работы с модулем NRF24L01

    //ПРИЕМНИК

    #define PIN_CE 10               // Номер пина Arduino, к которому подключен вывод CE радиомодуля

    #define PIN_CSN 9               // Номер пина Arduino, к которому подключен вывод CSN радиомодуля


    RF24 radio(PIN_CE, PIN_CSN);    // Создаём объект radio с указанием выводов CE и CSN


    #include <Wire.h>               // Библиотека для работы с шиной 1-Wire

    #include <LiquidCrystal_I2C.h>  // Библиотека для работы с ЖКИ


    LiquidCrystal_I2C lcd(0x27,16,2);

    float art[4][2];                  // Массив координат
    float rx[2];                      // Массив для приема

    byte message;                     // Эта переменная для обратного сообщения передатчику

    void setup(){

      lcd.init();
      lcd.clear();
      lcd.backlight();                // Включаем подсветку

      radio.begin();                  // Инициализация радиомодуля NRF24L01
      delay(1000);
      radio.setPayloadSize(32);
      radio.setChannel(5);            // Обмен данными будет вестись на пятом канале (2,405 ГГц)
      radio.setRetries(0,5);          // Кол-во попыток отправить данные
      radio.setDataRate (RF24_1MBPS); // Скорость обмена данными 1 Мбит/сек
      radio.setPALevel(RF24_PA_HIGH); // Выбираем высокую мощность передатчика (-6dBm)
      radio.setCRCLength(RF24_CRC_8);
      radio.enableAckPayload();       // Разрешение отправки нетипового ответа передатчику;
      radio.openReadingPipe(1,0x7878787878LL);      // Открываем трубу приема
      radio.openWritingPipe (0xE8E8F0F0ABLL);       // Открываем трубу передачи
      radio.powerUp();
      delay (500);
      //radio.startListening();                       // Включаем прослушивание открытой трубы
     
    }


    void loop(){

      for (int num=0; num<4; num++) {
     
      radio.startListening();                       // Включаем прослушивание открытой трубы
      delay (25);
        if(radio.available()){                                  // Если по радиоканалу поступили данные
        //bool done = true;
       
          //while (done) {
              radio.read(&rx,sizeof(rx));                           // Чтение буффера приема
              radio.stopListening();                        // Выключаем прослушивание открытой трубы
          //}

          message=num;                                            // сообщение ответа передатчику
          radio.writeAckPayload( 1, &message, sizeof(message) );  // Грузим сообщение для автоотправки
         
            art[num][0]=rx[0];          // заполняем массив координат        
            art[num][1]=rx[1];

            lcd.setCursor(0,0);
            //lcd.print(rx[0]);
            lcd.print("lAT["); lcd.print(num); lcd.print("]=");
            lcd.print(art[num][0]);
            lcd.setCursor(0,1);
            //lcd.print(rx[1]);
            lcd.print("LONG["); lcd.print(num); lcd.print("]=");
            lcd.print(art[num][1]);
            delay (500);
            lcd.clear();
           
        }
      }        
    }

    Код (C++):
    #include <SPI.h>;                     // Библиотека для работы с шиной SPI

    #include <nRF24L01.h>;                // Файл конфигурации для библиотеки RF24

    #include <RF24.h>;                    // Библиотека для работы с модулем NRF24L01

    // ПЕРЕДАТЧИК

    #define PIN_CE 10                     // Номер пина Arduino, к которому подключен вывод CE радиомодуля

    #define PIN_CSN 9                     // Номер пина Arduino, к которому подключен вывод CSN радиомодуля


    RF24 radio(PIN_CE, PIN_CSN);          // Создаём объект radio с указанием выводов CE и CSN

    byte message=255;                     // Эта переменная для сбора обратного сообщения от приемника

    float sending[2];                     // Массив для отправки

    float art[4][2]={                     // Массив координат
      {55.75, 59.938732},
      {48.7081906, 56.328571},
      {47.2213858, 44.6245814},
      {45.0433245, 45.0352566}
    };

    void setup(){

      Serial.begin(9600);                           // Инициализация серийного порта для отладки
      radio.begin();                                // Инициализация радиомодуля NRF24L01
      delay(1000);
      radio.setPayloadSize(32);
      radio.setChannel(5);                          // Обмен данными будет вестись на пятом канале (2,405 ГГц)
      radio.setRetries(0,5);                        // Кол-во попыток отправить данные
      radio.setDataRate (RF24_1MBPS);               // Скорость обмена данными 1 Мбит/сек
      radio.setPALevel(RF24_PA_HIGH);               // Выбираем высокую мощность передатчика (-6dBm)
      radio.setCRCLength(RF24_CRC_8);
      radio.enableAckPayload();
      radio.openWritingPipe (0x7878787878LL);       // Открываем трубу передачи
      radio.openReadingPipe(1,0xE8E8F0F0ABLL);      // Открываем трубу приема
      radio.powerUp();
      radio.stopListening();                        // Выключаем прослушивание открытой трубы
     
    }


    void loop(){
     
      for (int num=0; num<4; num++) {
        sending[0]=art[num][0];
        sending[1]=art[num][1];
          while (message!=num) {
          //radio.stopListening();                        // Выключаем прослушивание открытой трубы
          //delay (15);
          radio.write(&sending,sizeof(sending));      // Передаём по радиоканалу
          Serial.print("отправил Lat["); Serial.print(num); Serial.print("]:"); Serial.println(sending[0]);
          Serial.print("отправил Long["); Serial.print(num); Serial.print("]:"); Serial.println(sending[1]);
          Serial.print("ответ приемника:"); Serial.println(message);
          //radio.startListening();                       // Включаем прослушивание открытой трубы
          //delay (15);
            if ( radio.isAckPayloadAvailable() ) {      // Ждем получения...
            radio.read(&message,sizeof(message));       // читаем переменную message от приемника.
            }
            delay (1000);
          }
       
      }
    }
     
  20. parovoZZ

    parovoZZ Гуру

    Вот инициализация, в которой работает автоответ.
    Код (C++):
        //.. Инициализация трансивера
    void nRF_Init(void)
    {
         uint8_t Buf[5];

        //.. CONFIG
        Buf[0] =    (0<<nRF_MASK_RX_DR) | (0<<nRF_MASK_TX_DS) | (0<<nRF_MASK_MAX_RT) |    // маски прерываний от событий
    #if (!DIMMER)
            (0<<nRF_PRIM_RX) |                                // Режим передатчика
    #else
            (1<<nRF_PRIM_RX) |                                // Режим приемника
    #endif
            (1<<nRF_EN_CRC) | (0<<nRF_CRCO) |                // Проверка CRC разрешена, 1 байт CRC
            (1<<nRF_PWR_UP);                                // Запускаем трансивер
        SPI_WriteArray(nRF_WR_REG(nRF_CONFIG), 1, Buf);        // Отправляем команду. Пин CSN удерживается внутри функции

        //.. RF_CH  Настройка канала
        Buf[0] = nRF_channel;                                // Установка частоты канала передачи
        SPI_WriteArray(nRF_WR_REG(nRF_RF_CH), 1, Buf);        // см. Global_Settings.h

        //.. RF_SETUP  Настройки радиоканала
        Buf[0] = (0<<nRF_RF_DR) | ((0x03)<<nRF_RF_PWR0);    // Скорость передачи 1 Mbps, мощность: 0dbm
        SPI_WriteArray(nRF_WR_REG(nRF_RF_SETUP), 1, Buf);      
       
        Buf[1] = RX_ADDR_msb;                  
        Buf[2] = RX_ADDR_msb;
        Buf[3] = RX_ADDR_msb;
        Buf[4] = RX_ADDR_msb;

    #if (DIMMER)                // Если приемник
        //.. RX_ADDR_P0  Адрес канала 0 приемника
        Buf[0] = RX_ADDR_P0;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P0), 5, Buf);
    /*
        //.. RX_ADDR_P1  Адрес канала 1 приемника
        Buf[0] = RX_ADDR_P1;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P1), 5, Buf);

        //.. RX_ADDR_P2  Адрес канала 2 приемника
        Buf[0] = RX_ADDR_P2;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P2), 1, Buf);

        //.. RX_ADDR_P3  Адрес канала 3 приемника
        Buf[0] = RX_ADDR_P3;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P3), 1, Buf);

        //.. RX_ADDR_P4  Адрес канала 4 приемника
        Buf[0] = RX_ADDR_P4;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P4), 1, Buf);

        //.. RX_ADDR_P5  Адрес канала 5 приемника
        Buf[0] = RX_ADDR_P5;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P5), 1, Buf);
    */

        //.. FEATURE  Опции
        Buf[0] = (1<<nRF_EN_DPL) | (1<<nRF_EN_ACK_PAY);                // Разрешаем данные
        SPI_WriteArray(nRF_WR_REG(nRF_FEATURE), 1, Buf);            // переменной длины

        //.. EN_AA  Автоматическая отправка ACK о приеме данных по каналу
        Buf[0] = (1<<nRF_ENAA_P0);
        SPI_WriteArray(nRF_WR_REG(nRF_EN_AA), 1, Buf);

        //.. DYNPD  Прием данных переменной длины
        Buf[0] = (1<<nRF_DPL_P0);
        SPI_WriteArray(nRF_WR_REG(nRF_DYNPD), 1, Buf);

        //.. EN_RXADDR  Используемые каналы приемника
        Buf[0] = (1<<nRF_ERX_P0);
        SPI_WriteArray(nRF_WR_REG(nRF_EN_RXADDR), 1, Buf);

    /*    //.. Длина принимаемых данных по каналам
        Buf[0] = nRF_SEND_LEN;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P0), 1, Buf);
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P1), 1, Buf);
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P2), 1, Buf);
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P3), 1, Buf);
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P4), 1, Buf);
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P5), 1, Buf);*/

    #endif

    #if (!DIMMER)                                                // Если кнопка
        //.. RX_ADDR_P0  Адрес канала 0 приемника
        Buf[0] = RX_ADDR;                                        // На этот адрес будут приходить ACK пакеты
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P0), 5, Buf);

        //.. TX_ADDR  Адрес канала удаленного приемника
        SPI_WriteArray(nRF_WR_REG(nRF_TX_ADDR), 5, Buf);        // Адрес канала премника, на который будут уходить пакеты

        //.. EN_RXADDR  Используемые каналы приемника. На канале 0 принимается пакет автоподтверждения
        Buf[0] = (1<<nRF_ERX_P0)|(0<<nRF_ERX_P1)|(0<<nRF_ERX_P2)|(0<<nRF_ERX_P3)|(0<<nRF_ERX_P4)|(0<<nRF_ERX_P5);
        SPI_WriteArray(nRF_WR_REG(nRF_EN_RXADDR), 1, Buf);
    /*
        //.. Длина принимаемых данных по каналам
        Buf[0] = nRF_ACK_LEN;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P0), 1, Buf);
    */
         
        //.. SETUP_RETR  Настройка автоподтверждения
        Buf[0] = nRF_REPEAT_INTERVAL | nRF_REPEAT_MAX;            // Автоподтверждение и интервал повтора
        SPI_WriteArray(nRF_WR_REG(nRF_SETUP_RETR), 1, Buf);        // смотреть Settings.h

        //.. FEATURE  Опции
        Buf[0] = (1<<nRF_EN_DPL) | (1<<nRF_EN_ACK_PAY);            // Разрешаем прием данных
        SPI_WriteArray(nRF_WR_REG(nRF_FEATURE), 1, Buf);        // переменной длины

        //.. DYNPD  Прием данных переменной длины
        Buf[0] = (1<<nRF_DPL_P0)|(0<<nRF_DPL_P1)|(0<<nRF_DPL_P2)|(0<<nRF_DPL_P3)|(0<<nRF_DPL_P4)|(0<<nRF_DPL_P5);
        SPI_WriteArray(nRF_WR_REG(nRF_DYNPD), 1, Buf);

        //.. EN_AA  Автоматическая отправка ACK о приеме данных по каналу
    //    Buf[0] = (1<<nRF_ENAA_P0)|(0<<nRF_ENAA_P1)|(0<<nRF_ENAA_P2)|(0<<nRF_ENAA_P3)|(0<<nRF_ENAA_P4)|(0<<nRF_ENAA_P5);
    //    SPI_WriteArray(nRF_WR_REG(nRF_EN_AA), 1, Buf);

    #endif
    }
    А ещё в автоответ можно положить данные
    Код (C++):
        //.. Загрузить данные для отправки с пакетом ACK по каналу channel
    uint8_t nRF_ACK_Transmit(uint8_t *tx_data, uint8_t channel)
    {
        if (channel > 5) return 0;
        //<-- Вставить проверку FIFO
        SPI_WriteArray(nRF_W_ACK_PAYLOAD_MASK | channel, nRF_ACK_LEN, tx_data);

        return 1;
    }
    И они доберутся до передатчика без необходимости переводить последний в режим приёма.
     
    Un_ka нравится это.