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

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

  1. MakD

    MakD Нерд

    Всем доброго времени суток! Может быть кто сталкивался с ситуацией.
    В свое время был собран стенд из двух ардуино устройств с двумя передатчиками NRF24L01. Использовал библиотеку Mirf и в принципе все работало и вполне неплохо.
    В процессе доработки прототипа пришлось сменить одно устройство на Raspberry. В связи с чем мигрировал на библиотеку RF24. Это был тот еще цирк с конями. В общем все посты про емкости и начальную инициализацию я прошел. То есть конденсаторы на месте, параметры проинициализированы идентично. Запустить устройства получилось, однако, только в режиме setAutoAck(false) и disableCRC. Как только включаю True - тишина. В процессе экспериментов я кое-что заприметил. А именно:
    Используя библиотеку RF24 чтение буфера не приводит к его обнулению. То есть фактически буфер приходится принудительно сбрасывать, используя вот этот код:
    Код (C++):
      if(radio.available()){
        radio.read(&msg, sizeof(msg));
        radio.flush_rx();
      }
    Стоит отметить, что такое поведение наблюдается как раз в режиме setAutoAck(false) И (обязательное условие) disableCRC!!! Режим true проверить не удалось. Если входящий буфер не сбрасывать, то модуль клинит и в loop'е он постоянно выдает принятое в первом сообщении значение и более ничего не принимает.
    Может быть кто-то сталкивался? Не хотелось бы лезть внутрь самой библиотеки... Payload фиксированной длины.
     
  2. parovoZZ

    parovoZZ Гуру

    Отлистай несколько страниц назад - я здесь выкладывал абсолютно рабочий код со своей малинки.
     
  3. MakD

    MakD Нерд

    Проблема не в малинке. Проблема в Ардуино. Чтение буфера его не сбрасывает. Почему?
    Вариантов не много:
    Либо буфер прочитан не полностью, либо я чего-то недопонимаю. Но я использую пакеты фиксированной длины (9 байт). И в приемнике и в передатчике AutoAck и CRC отключены.
    В общем, здравствуйте регистры - буду разбираться с ними...

    Да, и еще момент. Откатился до 80-ой страницы и не нашел код для малинки... Но это не принципиально.
     
  4. parovoZZ

    parovoZZ Гуру

    Ну где-то я его выкладывал же. Как сейчас помню =)
    Код (C++):
    #include <cstdlib>
    #include <iostream>
    #include <fstream>
    #include <time.h>
    #include <RF24/RF24.h>

    #define nRF_SEND_LEN 11
    #define interrupt Pin RPI_V2_GPIO_P1_29
    using namespace std;

    typedef struct
      {
        uint8_t        id;                // 1 byte
        uint16_t    temperature;    // 2 byte
        uint16_t    pressure;       // 2 byte
        uint8_t        humidity;        // 1 byte
        uint8_t        rain;           // 1 byte
        uint8_t        solar;          // 1 byte
        uint8_t        wind;            // 1 byte
        uint8_t        direction_w;    // 1 byte
        uint8_t        _error;         // 1 byte
      } payload;                    // 11 byte

    // gpio pins, spi speed
    RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ);
    void getWeather(uint8_t Buf[nRF_SEND_LEN])
    {
        float temperature;
      //  bool    listen = 1;
        payload receivePayload;
        const time_t curTime = time(NULL);

        receivePayload.id = Buf[0];
        receivePayload.temperature = (uint16_t)Buf[2];
        receivePayload.temperature = ((receivePayload.temperature << 8) & 0xff00) | (uint16_t)Buf[1];
        receivePayload.pressure = (uint16_t)Buf[4];
        receivePayload.pressure = ((receivePayload.pressure << 8) & 0xff00) | (uint16_t)Buf[3];
        receivePayload.humidity = Buf[5];
        receivePayload.rain = Buf[6];
        receivePayload.solar = Buf[7];
        receivePayload.wind = Buf[8];
        receivePayload.direction_w = Buf[9];
        receivePayload._error = Buf[10];

        /*        if (Tempr & 0x80){                                        //Если отрицательная то в дополнительном коде
        sputc('–', DIGIT3);
        Tempr = ~(Tempr & 0x7f)+1;  */

       
        if (receivePayload.temperature & 0xf800){
            receivePayload.temperature = ~(receivePayload.temperature)+1;
            temperature = 0 -  (float)receivePayload.temperature / 16.0;
            }
        else
            {
            temperature = (float)receivePayload.temperature / 16.0;
            }

        printf("%s", ctime(&curTime));
        printf("0=%x, 1=%x, 2=%x, 3=%x, 4=%x, 5=%x, 6=%x, 7=%x, 8=%x, 9=%x, 10=%x\n", Buf[0], Buf[1], Buf[2], Buf[3], Buf[4], Buf[5], Buf[6], Buf[7], Buf[8], Buf[9], Buf[10]);
        printf("id=%d, temperature=%f, error=%d\n", receivePayload.id, temperature, receivePayload._error);
        printf("\n");

        ofstream out("/home/pi/Weather");

        out << "time" << ctime(&curTime) << "\n";
        out << "ID = " << receivePayload.id*1 << "\n";
        out << "temperature = " << temperature << "\n";
        out << "humidity = " << receivePayload.humidity*1 << "\n";
        out << "pressure = " << receivePayload.pressure << "\n";
        out << "solar = " << receivePayload.solar*1 << "\n";
        out << "wind = " << receivePayload.wind*1 << "\n";
        out << "direction = " << receivePayload.direction_w*1 << "\n";
        out << "rain = " << receivePayload.rain*1 << "\n";
        out << "text = " << receivePayload._error*1 << "\n";

        out.close ();
    }

    void intHandler()
    {
        uint8_t pipe_num=0;
        uint8_t Buf[nRF_SEND_LEN];
        bool tx_ok, tx_fail, rx;

        radio.whatHappened(tx_ok, tx_fail, rx);

        if (radio.available(&pipe_num)){
            memset(Buf, 0, nRF_SEND_LEN);
            radio.read(Buf, nRF_SEND_LEN);


            switch (Buf[0]){

                case 222:
                getWeather(Buf);
                break;
        }
               
        radio.stopListening();
        radio.startListening();

        }
    }

    int main(int argc, char** argv)
    {

      radio.begin();
      radio.setPALevel(RF24_PA_MAX);
      radio.setDataRate(RF24_1MBPS);
      radio.setCRCLength(RF24_CRC_8);
      radio.setRetries(15, 15);
      radio.setAutoAck(1);
      radio.setChannel(125);
      radio.setPayloadSize(nRF_SEND_LEN);
      radio.openWritingPipe(0x1111111111LL);
      radio.openReadingPipe(1,0x1111111110LL);
      radio.openReadingPipe(2,0x1111111111LL);
      radio.openReadingPipe(3,0x1111111112LL);
      radio.openReadingPipe(4,0x1111111113LL);
      radio.openReadingPipe(5,0x1111111114LL);
      //radio.maskIRQ(0, 0, 1);
      radio.startListening();
      radio.printDetails();

      attachInterrupt(interruptPin, INT_EDGE_FALLING, intHandler);

      while (1)
      { delay(100);

      }
         
    }
    Код писался по юности))), поэтому он и рабочий, но сейчас бы я его писал бы по-другому...Возможно, когда-то я это и сделаю. Здесь есть, что улучшить)))
     
    MakD нравится это.
  5. parovoZZ

    parovoZZ Гуру

    Поверь мне - с ними гораздо проще. Узнать длину сообщения можно командой R_RX_PL_WID. Возможно, что читаешь не весь буфер в слоте. Он 100% должен сброситься после чтения. Биты RX_P_NO в регистре статуса показывает номер трубы, по которой принято сообщение. Если все три слота пусты, то биты будут выставлены в b111.
     
    MakD нравится это.
  6. MakD

    MakD Нерд

    Очень даже лаконично. Я на малинке решил на питоне писать для развлечения (требований по быстродействию нет). Но по сути - один в один...
    Все же мне кажется, что проблема у меня на стороне приемника. Надо в регистрах поковыряться, что же такое в них лежит...
     
  7. Miller_VA

    Miller_VA Нерд

    Только что, на Ваших глазах, ну и с Вашим участием, был побеждён и гад работает как часы(китайские), модуль nRF24L01+ прикрученный к ATMege16. Под Arduino Nano я проскочил очень легко. Об этом я уже писал. Там заработало всё быстро и далеко. Возникли некоторые трудности при подключении к плате на ATMege16. На плате полно всякой всячины, только вот радио-передатчика не хватало. Оно мне надА! НадА заказчику. Задача даже простая, накопив показания прибор должен по беспроводной связи сбросить их на комп. Это сделаем. Потом. Сейчас не мог получить от передатчика даже простую комбинацию данных. Тех, которые посылал. ....
    Но потом, вдруг раз и всё получилось. Впереди новые проблемы о которых я ещё не знаю. Сработало два закона жизни, которые нарушать нельзя. Первый: "Учить мат-часть!". Второй: "Библию нужно читать до конца".
    Пришлось в библиотеке для nRF24 дописать вывод всех регистров. Без всякой расшифровки. Тупо содержимое.В своей библиотеке такую-же процедуру. Сидел сравнивал. И после того, как я понял, что всё одинаково для приёмников(у меня стоят рядом два приёмника на разных платах, приём от одного передатчика) и я знаю, что и зачем туда записано, только после этого всё заработало. Последний штрих в программе - забыл про авто Ask. Последние регистры не вывел на экран, вот и возился с этим тоже. Питание, короткие провода, конденсаторы, менять местами платы, питание от отдельных супер источников, бессонная ночь - всё это пройдено. Но не очень и помогало. О железе. Передатчик Arduino Nano + nRF24L01+, висит на USB, bRf24 запитан через L1117 от 5в. с Arduino. Один приёмник Arduino Nano + nRF24L01+, 3.3в питания от Arduino. Второй приёмник ATMega 1 + nRF24L01+, питание через L1117. И всё это питается от USB. Тут - же подключен отладчик ISP для ATMega16(пишу программы, отлаживаю в среде IAR). .. Устал писать. Если что, я тут.
     
  8. parovoZZ

    parovoZZ Гуру

    Чтобы сидеть не сравнивать, я завожу один файл nRF24l01p.c и для приемной стороны и для передающей. Внутри все функции абсолютно одинаковые. А вот переменные распределяю через команды препроцессора
    #if, #elif, #ifndef.... Ещё есть один файл с глобальными настройками - там указываю канал, все адреса, скорость.
     
  9. Miller_VA

    Miller_VA Нерд

    Сравнивал содержимое в регистрах nRf24. Мне больше нравиться смотреть, что там внутри микросхемы. Что прописано, какие биты в 0 и 1. Например, с обменом по SPI вообще проблем не было. Брал за основу библиотеку (просто попалась, решил что подходит) для своего прибора, а там программный SPI. Перевёл на аппаратный. Его-же надо быстренько протестировать. Запихнул всего разного. Сбоев нет для максимальной скорости, значит всё в норме. А ещё хорошо по шагам пошагать. Один раз медленно пройтись по программе быстрее получается. Я же под IARом работаю, почему не пользоваться всеми его прелестями? Кстати, на сегодня впечатление такое. Хоть и капризная сучка (так люди пишут про nRf24), но очень даже послушная. Что ей задашь, то она и делает. Много я ещё не пробовал, но с на любых трубах, скоростях, с авто Аском и без, с CRC и без, на 300м. с антенной, тест на количество пропущенных пакетов, работа по прерываниям и без, и ещё чего, не помню, всё прошла. Пока буду продолжать, а там посмотрим.
     
  10. parovoZZ

    parovoZZ Гуру

    Надо все делать тупо по даташиту и она будет работать и именно так, как и задумано.
     
  11. Miller_VA

    Miller_VA Нерд

    Я так и хотел, тонко намекнуть. Многим читателям данного форума. Ребята там всё просто, всё как написано, читайте, делайте, всё получиться. Освоение(ну пока не полное) прошло стандартно. Даже не интересно(могу анекдот рассказать :) , много всяких вещей уже освоено. Вначале непонятно, всё не работает, потом почему-то заработало, непонятно почему, и последнее, работает даже выключеное!!!!
     
  12. parovoZZ

    parovoZZ Гуру

    Там самое главное - выдерживать минимальные интервалы.
     
  13. ImrDuke

    ImrDuke Гик

    У меня никак не получится по разным каналам принять информацию.
    Два передатчика и один приемник. Если трубы оба передают по одной трубе, то все хорошо. Пытаюсь передать с двух разных, работает только первая.
    Может я что то упускаю?
    Код (C++):

    void RadioSetup() {
      radio.begin();                           // активировать модуль
      radio.setCRCLength(RF24_CRC_8);          // Использовать CRC-8
      radio.setAutoAck(1);                     // режим подтверждения приёма, 1 вкл 0 выкл
      radio.setRetries(15, 15);                // (время между попыткой достучаться, число попыток)
      radio.enableAckPayload();                // разрешить отсылку данных в ответ на входящий сигнал
      radio.setPayloadSize(32);                // размер пакета, в байтах
      radio.openReadingPipe(1, 0x3A3A3A3AC3LL);
      radio.openReadingPipe(2, 0x3A3A3A3AD2LL);
      radio.setChannel(CH_NUM);                // выбираем канал (в котором нет шумов!)
      radio.setPALevel(SIG_POWER);             // УРОВЕНЬ МОЩНОСТИ RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
      radio.setDataRate(SIG_SPEED);            // СКОРОСТЬ ОБМЕНА RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
      radio.powerUp();                         // начать работу
      radio.startListening();                  // начинаем слушать эфир, мы приёмный модуль
    }
    Код (C++):
    void loop() {
      uint8_t pipe_num = 0;
      if (radio.available(&pipe_num)) {
        memset(Received_Data, 0, sizeof(Received_Data));     // Обнуляем массив
        while (radio.available(&pipe_num)) {
          radio.read(&Received_Data, sizeof(Received_Data));   // Читаем входящий сигнал
        }
        radio.flush_rx();
        DevID = Received_Data[0];                            // Номер устройства
    }
    }
     
  14. Miller_VA

    Miller_VA Нерд

    Давайте ещё передатчик посмотрим.
     
  15. MakD

    MakD Нерд

    Всем доброго дня! Изучая даташит, возникло чувство легкого недопонимания. Есть такой режим работы модуля, как AutoAck. Этот режим поддерживается на железячном уровне для NRF24L01+. В режиме передачи пакет состоит из следующих частей: Преамбула (1 байт), Адрес (3-5 байт), Пакет контрольных полей (9бит), Тело сообщения (0-32 байта), CRC (1-2 байта).
    Адрес - это адрес получателя, то есть кому, собственно, предназначается сообщение. А когда получатель принимает сообщение, он должен отправить пакет подтверждения. Насколько я понимаю, вышеописанная структура сообщения относится и к пакетам подтверждения. Но возникает вопрос, а чем будет заполнена вторая часть пакета подтверждения (это адрес)? В случаях, когда в системе всего два передатчика я бы мог предположить, что получатель возьмет адрес из openWritingpipe(). А что будет в том случае, если у меня 3 модуля в схеме? Кто-нибудь изучал данный вопрос детально?
     
  16. parovoZZ

    parovoZZ Гуру

    Я сейчас с телефона, поэтому без цитат. В пакете Ack адрес формируется тот же, что и у pipe, которая приняла пакет. Это приёмник. Передатчик же этот пакет ждёт на pipe0, таким образом, адреса TX и RX у передатчика для нулевого pipe должны совпадать. Пакет Ack - это самый обычный пакет со всеми структурами, НО. Если мы в пакете Ack не передаём никакие данные, то эта структура будет пустой, а счётчик данных будет равен нулю. Кроме того, в пакете есть бит Ack, который показывает, надо слать автоподтверждение или не надо. Так вот в пакете Ack он также равен нулю. Передатчиков может быть много и есть здесь ровно два варианта - либо они все вещают на один адрес pipe у пртемника, либо же на разные. Всего их 6. Pipe чисто программные, поэтому обмен одновременно по разным pipe невозможен.
     
    MakD нравится это.
  17. MakD

    MakD Нерд

    Это просто карма какая-то. Я перечитал весь даташит два раза вдоль и поперек! Я последовал совету и прогнал выгрузку всех регистров на тестовом скетче. В результате я убрал flush_rx и включил AutoAck(true) с CRC16, запустил прерывание. Все заработало как часы. Большое спасибо!!! Следующим этапом буду выводить МК из сна по прерыванию...
     
  18. parovoZZ

    parovoZZ Гуру

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

    Вложения:

    • nRF24L01P_Reg.h
      Размер файла:
      8,7 КБ
      Просмотров:
      368
    DetSimen нравится это.
  19. parovoZZ

    parovoZZ Гуру

    тю. Для начала трансивер в сон засунь...
     
  20. ImrDuke

    ImrDuke Гик

    Код (C++):

    void RadioSetup() {
      radio.begin();                         // активировать модуль
      radio.setCRCLength(RF24_CRC_8);        // Использовать CRC-8
      radio.setAutoAck(1);                   // режим подтверждения приёма, 1 вкл 0 выкл
      radio.setRetries(15, 15);              // (время между попыткой достучаться, число попыток)
      radio.enableAckPayload();              // разрешить отсылку данных в ответ на входящий сигнал
      radio.setPayloadSize(32);              // размер пакета, в байтах
      radio.openWritingPipe(0x3A3A3A3AC3LL);        // открываем канал для передачи данных
      radio.setChannel(CH_NUM);              // выбираем канал (в котором нет шумов!)
      radio.setPALevel(SIG_POWER);           // УРОВЕНЬ МОЩНОСТИ RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
      radio.setDataRate(SIG_SPEED);          // СКОРОСТЬ ОБМЕНА RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
      radio.powerUp();                         // начать работу
      radio.stopListening();                 // не слушаем радиоэфир, мы передатчик
    }
    Код (C++):
    void loop() {
      Transmit(digitalRead(btnAlarm));
    }

    void Transmit(boolean _Flag) {
      noInterrupts();
      dht.begin();
      radio.powerUp();                                       // Включить передатчик
      radio.stopListening();                                 // не слушаем радиоэфир, мы передатчик
      memset(Transmit_Data, 0, sizeof(Transmit_Data));       // Обнуляем массив
      Transmit_Data[0] = DeviceID;                           // Номер устройства
      if (!isnan(dht.readTemperature())) {
        Transmit_Data[1] = dht.readTemperature() * 100;      // Температура
        Transmit_Data[2] = dht.readHumidity()    * 100;      // Влажность
      }
      else {
        Transmit_Data[1] = NAN;
        Transmit_Data[2] = NAN;
      }
      Transmit_Data[3] = (float)(1.1 * 16368) / Vbg() * 100; // Напряжение аккум.
      Transmit_Data[4] = _Flag;                              // Геркон
      Transmit_Data[5] = 0;                                  // Реле
      radio.write(&Transmit_Data, sizeof(Transmit_Data));    // Отправить по радио
      radio.powerDown();                                     // Выключить передатчик
      interrupts();
    }