Однажды, в далекой далекой.... Завалялась у меня дома два модуля NRF24L01. Дружба с ними не заладилась как-то сразу. С тех прошло около десяти лет и вот я их снова нашел.
Для них как раз есть актуальная задача: необходимо с минимальным потреблением передавать одиночные пакеты информации на приличное расстояние (около 600 метров). Да тут еще и статья интересная попалась на глаза... ссылка.
Примечание: Многие фотографии и скрины делались уже после прохождения пути.
Описание оборудования и полезная информация
Вот такой радиопередатчик
Изображение
Вот такая ардуино
Изображение
Вот такая схема подключения
Изображение
Вывод модуля | Вывод ардуины | Цвет провода на схеме |
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. Попытка поймать хотя бы байт информации
Время поджимало, хотелось скорее начать использовать. Заподозрил в разности частот сами ардуины.
Одна из них была искра.
Изображение
И она работала на другой частоте, нежели чем обычная китайская (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В.
График зависимости напряжения от тока
Посмотрел на потребляемый ток у модуля
Требуемое напряжение питания и типовой потребляемый ток
Ну и наконец решил радикально отфильтровать питание.
Фильтрация по питанию
Напряжение, приходящее на модуль радиопередатчика упало с 3,3В до 3,1В.
Зато исчезла пульсация.
Итог
Получил вот такую статистику
Теперь работает на всех скоростях и на всех мощностях. Платы ардуино перестали грется. На дальность не проверял, но по комнате около 10 метров со стальными стелажами ловит уверенно. На действительное потребление тоже не проверял.
Всем спасибо за внимание и успехов во всех начинаниях.
Список источников: