Как стать автором
Обновить

Долгий путь к подключению NRF24L01

Время на прочтение9 мин
Количество просмотров17K

Однажды, в далекой далекой.... Завалялась у меня дома два модуля NRF24L01. Дружба с ними не заладилась как-то сразу. С тех прошло около десяти лет и вот я их снова нашел.

Для них как раз есть актуальная задача: необходимо с минимальным потреблением передавать одиночные пакеты информации на приличное расстояние (около 600 метров). Да тут еще и статья интересная попалась на глаза... ссылка.

Примечание: Многие фотографии и скрины делались уже после прохождения пути.

Описание оборудования и полезная информация

Вот такой радиопередатчик

Изображение
Распиновка модуля NRF24L01
Распиновка модуля NRF24L01

Вот такая ардуино

Изображение
Arduino Nano распиновка
Arduino Nano распиновка

Вот такая схема подключения

Изображение

Вывод модуля

Вывод ардуины

Цвет провода на схеме

GND

GND

черный

VCC

3.3В

красный

CE

D9

зеленый

CSN

D10

серый

SCK

D13

желтый

MOSI

D11

синий

MISO

D12

фиолетовый

IRQ

---

---

Шаг 0. В библиотеке есть файл примера...

И называется он GettigStarted. С ним у меня вообще ничего не вышло. Совсем.

Шаг 1. Пффф, да есть же мануал на хабре, где все понятно и работает

Собираю все на проводах, прозваниваю. Заливаю в одну ардуинку скетч передатчика, в другую скетч сканера эфира. В терминале вижу примерно следующее...

RF24/examples/scanner/

00000000000000001111111111111111222222222222222233333333333333334444444444444444555555555555555566666666666666667777777777777777

0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef

01000000000000000000011211611100123555773210000100000243000100000001026800010122011000000000000122112000000000000000000000000000

01320000000000000010012102101100277668344110000000000050010000000000111200000001122000000000000111111000000000000000000000000000

А передавал-то я на частоте 0x7B....

С выключенным передатчиком картина такая же....

Думаю: ну вот, точно бракованные модули, можно спать спокойно. Но жадность взяла своё, решил их терзать дальше. Начитался про то, что все вешают конденсатор. Повесил тоже. На обе.

Шаг 2. Первые признаки жизни...

Заподозрил что проблема в питании. И решил уменьшить скорость передачи, мощность передачи и частоту посылок.

Код передатчика

Текст кода передатчика
#include <SPI.h>
#include <RF24.h>
#include "printf.h"
RF24 radio(9, 10); // порты D9, D10: CSN CE
const uint32_t pipe = 111156789; // адрес рабочей трубы; 111156789

byte data;
void setup() {
  Serial.begin(57600);
  printf_begin();
  Serial.println("TransmitterTester ON "); //Оповещаем о включении ардуины
  radio.begin();                    	// Запускаем радиомодуль
  delay(3000);												// Ждем загрузку и стабилизации его работы
  radio.setDataRate(RF24_250KBPS);  	// Скорость обмена данными RF24_1MBPS или RF24_2MBPS (RF24_250KBPS, RF24_1MBPS, RF24_2MBPS)
  radio.setCRCLength(RF24_CRC_8);   	// Размер контрольной суммы 8 bit или 16 bit
  radio.setPALevel(RF24_PA_MIN);    	// Уровень питания усилителя RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX ((RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBm, RF24_PA_MAX=0dBm).
  radio.setChannel(0x62);           	// Установка канала
  radio.setAutoAck(false);          	// Отключаем автоответ на пакеты
  radio.powerUp();                  	// Включение или пониженное потребление powerDown - powerUp
  radio.stopListening();            	// Остановить прослушивание радиоэфира
  radio.printDetails();								// Вывод текущих параметров радиомодуля
  radio.openWritingPipe(pipe);      	// открыть трубу на отправку
  Serial.println("Start send...");  	// Оповещаем о начале работы
}
void loop() {
  data = 109;
  radio.write(&data, 1);
  delay(24);
  //Serial.println("data= " + String(data));
}

Код сканера

Текст кода сканера
/*
 Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 version 2 as published by the Free Software Foundation.
 */

/**
 * Channel scanner
 *
 * Example to detect interference on the various channels available.
 * This is a good diagnostic tool to check whether you're picking a
 * good channel for your application.
 *
 * Inspired by cpixip.
 * See http://arduino.cc/forum/index.php/topic,54795.0.html
 */

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

//
// Hardware configuration
//

// Set up nRF24L01 radio on SPI bus plus pins 9 & 10

RF24 radio(9,10);

//
// Channel info
//

const uint8_t num_channels = 128;
uint8_t values[num_channels];

//
// Setup
//

void setup(void)
{
  //
  // Print preamble
  //

  Serial.begin(57600);
  printf_begin();
  printf("\n\rRF24/examples/scanner/\n\r");

  //
  // Setup and configure rf radio
  //

  radio.begin();
  radio.setAutoAck(false);

  // Get into standby mode
  radio.startListening();
  radio.stopListening();

  // Print out header, high then low digit
  int i = 0;
  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");
}

//
// Loop
//

const int num_reps = 100;

void loop(void)
{
  // Clear measurement values
  memset(values,0,sizeof(values));

  // Scan all channels num_reps times
  int rep_counter = num_reps;
  while (rep_counter--)
  {
    int i = num_channels;
    while (i--)
    {
      // Select this channel
      radio.setChannel(i);

      // Listen for a little
      radio.startListening();
      delayMicroseconds(128);
      radio.stopListening();

      // Did we get a carrier?
      if ( radio.testCarrier() )
        ++values[i];
    }
  }

  // Print out channel measurements, clamped to a single hex digit
  int i = 0;
  while ( i < num_channels )
  {
    printf("%x",min(0xf,values[i]&0xf));
    ++i;
  }
  printf("\n\r");
}

// vim:ai:cin:sts=2 sw=2 ft=cpp

Необходимый файлик printf.h

Текст внешней функции печати
/*
 Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 version 2 as published by the Free Software Foundation.
 */
 
/**
 * @file printf.h
 *
 * Setup necessary to direct stdout to the Arduino Serial library, which
 * enables 'printf'
 */

#ifndef __PRINTF_H__
#define __PRINTF_H__

int serial_putc( char c, FILE * ) 
{
  Serial.write( c );

  return c;
} 

void printf_begin(void)
{
  fdevopen( &serial_putc, 0 );
}

#endif // __PRINTF_H__

Ситуация резко изменилась...


RF24/examples/scanner/

00000000000000001111111111111111222222222222222233333333333333334444444444444444555555555555555566666666666666667777777777777777

0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef

010000000000000000000112116111001235557732100001000002430001000000010268000101220110000000000001221120000001e4520000000000000000

013200000000000000100121021011002776683441100000000000500100000000001112000000011220000000000001111110000004a9100000000000000000

00200000000000000100011303201120035454455400000001000141000000100000013100000230100000000000000012221000000845300000000000000000

10210000011000000101000013102000023343245330000000000141000000000000032700000112210000000000000112112000000625600000000000000000

000000000100000000000220012210002355344432300000001003410000010000000433000001120200000000000002121120000008b7800000000000000000

1100000000000000101002220210200013322343431000000010114200100000001002330000012101000000000000020022100000045ac00000000000000000

А передавал-то я на частоте 0x7B....

Шаг 3. Попытка поймать хотя бы байт информации

Время поджимало, хотелось скорее начать использовать. Заподозрил в разности частот сами ардуины.

Одна из них была искра.

Изображение
Внешний вид Arduino Iskra
Внешний вид Arduino Iskra

И она работала на другой частоте, нежели чем обычная китайская (Atmega328P с 8Мгц у китайской против 16Мгц на Atmega328PB у искры)

Собрал на двух одинаковых - без изменений.

Заменил провода на пайку - без изменений.

Накидал свой скетч, который перебирает каналы на каждом из которых пытается получить заданное количество посылок.

Текст кода сканера
#define TryingLimitDef 60 //Колличество попыток поймать посылку на каждом из каналов
#include <SPI.h>
#include <RF24.h>
#include "printf.h"
RF24 radio(9, 10); // порты D9, D10: CSN CE

const uint32_t pipe = 111156789; // адрес рабочей трубы;
byte data[1];
int scn;  //счетчик циклов прослушивания эфира
int sg;  //счетчик числа принятых пакетов с передатчика
int CHStart = 94; //С какого канала начинать
int CHto = 7; //Сколько каналов щелкать
int CH = CHStart; 
int TryingLimit = TryingLimitDef;
int CountOfTrying = 0;
int ArrPck[TryingLimitDef]; //Массив для вывода статистики по пакетам
int ArrName[TryingLimitDef]; //Массив с именами просканированных каналов
int ArrCarrier[TryingLimitDef]; //Массив значений анализа параметра Carrier
int ArrRPD[TryingLimitDef]; //Массив значений анализа параметра RDR
void setup() {
  Serial.begin(57600);
  printf_begin();
  Serial.println("ReceiverTester ON");
  Serial.println("Start Module ");
  radio.begin();
  delay(2000);
  Serial.println("Run... ");
  delay(500);
}
void loop() {
  Serial.print ("CH:"); Serial.print("\t");
  for (int i = 0; i <= CHto ; i++){
    ReSetupRF(CH);//Вызов перенастройки модуля
    CH++;
    CountOfTrying++;
    delay(850);
  }
  //Вывод результатов
  Serial.println ("");
  Serial.print ("PCG:"); Serial.print("\t");
  for (int i = 0; i <= CHto ; i++){
      Serial.print (String(ArrPck[i])); Serial.print("\t"); Serial.print("\t");
      ArrPck[i] = 0;
  }
  Serial.print(" of " + String(TryingLimit));
  Serial.println ("");
  Serial.print ("CRR:"); Serial.print("\t");
  for (int i = 0; i <= CHto ; i++){
      Serial.print (String(ArrCarrier[i])); Serial.print("\t"); Serial.print("\t");
      ArrCarrier[i] = 0;
  }
  Serial.print(" of " + String(TryingLimit));
  Serial.println ("");  
  Serial.print ("RPD:"); Serial.print("\t");
  for (int i = 0; i <= CHto ; i++){
      Serial.print (String(ArrRPD[i])); Serial.print("\t"); Serial.print("\t");
      ArrRPD[i] = 0;
  }
  Serial.print(" of " + String(TryingLimit));
  Serial.println ("");  Serial.println ("");
  //Сброс счетчиков
  CountOfTrying = 0;
  CH = CHStart;
}
void ReSetupRF(int CH){
  //Serial.println("ReSetup Run....");
  //radio.begin();
  //Serial.println("Set Speed ");
  radio.stopListening();
  radio.setDataRate(RF24_1MBPS); // скорость обмена данными RF24_1MBPS или RF24_2MBPS
  //Serial.println("Set CRC ");
  radio.setCRCLength(RF24_CRC_8); // размер контрольной суммы 8 bit или 16 bit 
  //Serial.print("Curr CH ");  Serial.println(CH, HEX);
  radio.setChannel(CH);         // установка канала
  //Serial.println("Set Ack ");
  radio.setAutoAck(false);       // автоответ
  // Serial.println("PW ");
  //radio.powerUp(); 
  //Serial.println("STOP "); 
  //radio.stopListening();  //delay(2000);
  //Serial.println("GET INFO "); 
  //radio.printDetails();
  //delay(2000);
  //Serial.println("DONE GET INFO ");
  //delay(1000);
  //Serial.println("Open Pipe ");
  radio.openReadingPipe(1, pipe); // открыть трубу на приём
  //Serial.println("Start Listening ");
  radio.startListening();        // приём
  delay(1000);
  PckCatch(); //Начать анализ эфира
}
void PckCatch(){
  //Serial.println("Start Catch...");
  //Вывести название анализируемого канала
  if(CH < 16){
    Serial.print("0x0");  Serial.print(CH, HEX); Serial.print("\t"); Serial.print("\t");
  }else{
    Serial.print("0x");   Serial.print(CH, HEX); Serial.print("\t"); Serial.print("\t");
  }
  //Очистка буферов радиомодуля, избавление от переходных процессов при перенастройки частоты
  for (int x = 0; x < 35 ; x++){    // прослушивание эфира, очистка буфера перед чистовым
    if (radio.available())
    {
      radio.read(data, 1);    }
    delay(5);
  }
  //Накопление информации
  for (int x = 0; x < TryingLimit ; x++){
// прослушивание эфира на несущую
if ( radio.testCarrier() ){
    ArrCarrier[CountOfTrying]++;
}

// прослушивание эфира на сигнал мощнее -64dBm
if ( radio.testRPD() ){
    ArrRPD[CountOfTrying]++;
}    

// прослушивание эфира на посылки
if (radio.available())
{
    radio.read(data, 1);           
    if (data[0] == 109) {
      sg++;
      //Serial.print("*"); 
    }else{
      //Serial.print("+"); 
    }
}
else{
  //Serial.print("."); 
}
delay(50);

  }
  //Serial.println("");
  if(sg > 0){
      //Serial.print("FOUND " + String(sg) + " of " + String(TryingLimit) + " ON CH:"); 
    if(CH < 16){
      //Serial.print("0x0"); Serial.println(CH, HEX); 
    }else{
      //Serial.print("0x"); Serial.println(CH, HEX);
    }
    //Serial.println("");
    delay(300);
  }else{
    //Serial.println("");
    //Serial.println("");
  }
  //Запись в массивы, сброс переменных
  ArrPck[CountOfTrying] = sg;
  ArrName[CountOfTrying] = CH;
  sg = 0;
  scn = 0;
  delay(700);
 }

Краткое пояснение:

TryingLimitDef - задает колличество поппыток анализа выбранной частоты

CHStart - задает с какого канала начинать поиск. Для удобства преобразования данных с простого сканера использовал перевод через калькулятор windows.

CHto - задает количество анализируемых каналов

Краткий алгоритм действий:

Описание

Включил передатчик. Нашел общим сканером.

например, возле этой частоты появились какие-то цифры.

0x2A перевожу в десятичную

Загружаю вместо сканера свой. Задаю CHStart 38 (на четыре меньше, чем где увидел сигнал) и ставлю искать на 10 каналов.

Проверяю на каких каналах есть посылки, если они есть.

Получил примерно следующее:

ReceiverTester ON
Start Module 
Run... 
CH:		0x78		0x79		0x7A		0x7B		0x7C		0x7D		0x7E		0x7F		
PCG:	8				19				1				1				5				1				0				0		 of 60
CRR:	34			59				35			65			55			8				0				0		 of 60
RPD:	23			59				23			21			55			2				0				0		 of 60

CH - канал, на котором проводилось измерение и захват посылок

PCG - колличество удачных попыток найти с верной CRC.

CRR - колличество удачных обнаружений несущей на выбранном канале

RPD - колличество удачных обнаружений несущей с уровнем сигнала выше -64Дбм

А передавал-то я на частоте 0x7B.... Но информация идёт!

При этом, если повысить мощность, то пакетов ловилось меньше. Если повысить скорость общения (1Мбит/с и выше), то сигнал исчезает полностью.

После этого совсем недоумевая я решил таки проверить питание.

Шаг 4. Все было очень просто...

Приложил мультиметр. Да не простой, а поверенный и даже не очень китайский. И увидел 3,3В

Решил подключить осциллограф и на разных частотах проверить всякие пульсации...

Фото осциллограммы
Пульсации по напряжению при передачи посылки
Пульсации по напряжению при передачи посылки

Но как же так! Ведь был установлен электролит на 100мкФ и 20В.

Посмотрел параметры преобразователя из 5В в 3,3В.

График зависимости напряжения от тока
Описание падения напряжения от тока согласно  тех. документации на AMS1117
Описание падения напряжения от тока согласно тех. документации на AMS1117

Посмотрел на потребляемый ток у модуля

Требуемое напряжение питания и типовой потребляемый ток
Заявленное потребление питания
Заявленное потребление питания
Заявленные токи питания
Заявленные токи питания

Ну и наконец решил радикально отфильтровать питание.

Фильтрация по питанию
Вольная импровизация на тему схемотехники
Вольная импровизация на тему схемотехники

Напряжение, приходящее на модуль радиопередатчика упало с 3,3В до 3,1В.

Зато исчезла пульсация.

Итог

Получил вот такую статистику

Статистика получения пакетов
Статистика получения пакетов

Теперь работает на всех скоростях и на всех мощностях. Платы ардуино перестали грется. На дальность не проверял, но по комнате около 10 метров со стальными стелажами ловит уверенно. На действительное потребление тоже не проверял.

Всем спасибо за внимание и успехов во всех начинаниях.

Список источников:

NRF24L01 datasheet

AMS1117 datasheet

Статья про подключение

Статья про подключение

Теги:
Хабы:
+11
Комментарии31

Публикации