Wi-Fi радиомодуль на основе nRF24L01

Описание

Wi-Fi радиомодуль основе чипа nRF24L01. Модули работают на частоте 2.4ггц и поддерживают скорость передачи до 2Mbps. Радиоканал на данных радиомодулях может обмениваться информацией в оба направления.

Встречается в двух исполнениях: nRF24L01 и nRF24L01+. Плюсовая версия более новая и отличается рядом характеристик (выше чувствительность, есть режим 250Kbps, добавлены некоторые режимы работы). На фотографии хорошо видны внешние различия: черный - это nRF24L01+, зеленый - nRF24L01.

Также изредка встречаются плюсовые модули разъемом для внешней антенной (саму антенну можно взять, например, со старого роутера). Для них заявленная дальность до 1100 метров при скорости 250кбит. При 1мбит - 750 метров. А при скорости 2мбит- 520 метров. Скорее всего эти параметры указаны при отсутствии препятствий и если все радиомодули в радиосети будут именно такие. Рекомендую уменьшать избыточную мощность через setPALevel (библиотека Nf24) или напрямую в регистре RF_SETUP.

Радиомодуль возможно подключить к любому микроконтроллеру, который имеет интерфейс SPI , а так же к микрокомпьютерам, например Raspberry PI.

Технические характеристики

  • Частота передачи/приема 2.4GHz.
  • Дальность до 100м. В помещении - до 30м.
  • Скорость до 2Mbps. Возможные варианты: 250kbps, 1Mbps и 2Mbps.
  • Интерфейс взаимодействия с микроконтроллером - SPI. Выводы толерантны к 5 вольтам.
  • Напряжение: 3-3.6В (рекомендуется 3,3В). При попытке подключения 5В вывод из строя маловероятен,но работать устройство отказывается..
  • Максимальная выходная мощность: 0dBm
  • Коэффициент усиления антенны (пиковая): 2dBm
  • Количество каналов: 126. Нулевой канал начинается с 2400MHz и далее с шагом 1MHz,например 70 канал находится соответственно на 2470MHz. При установке скорости 2Mbps занимается ширина двух каналов.

Назначение выводов

GNDVcc
CECSN
SCKMOSI
MISOIRQ
  • SCK (Serial ClocK) — тактирование (синхронизация).
  • MOSI / MI (Master Out Slave In) — вход данных.
  • MISO / MO (Master In Slave Out) — выход данных.
  • *CE/SS - Выбор ведомого на шине SPI из нескольких устройств.
  • SCN - выбор режима приема/передача , фактически тот же CE.
  • IRQ - выход прерывания,чаще всего не используется. Необходим для немедленной реакции микроконтроллера при приеме нового пакета данных.
  • GND - земля,масса,минусовая шина.
  • Vcc - питание модуля 3.3в. Для надежной работы паралельно питанию как можно ближе к радиомодулю рекомендуется подключить электролитический конденсатор не менее 100мкф , а так же керамический на 0.1 мкф.

Подключение nRF24L01+ к Arduino

Arduino UNOArduino Mega
SCK1352
MOSI1151
MISO1250
CSN10 (1)53 (2)
CE/SS9(1)48 (2)
  • (1) - при установке иницилизации в скетчке командой RF24 radio(9,10);
  • (2) - при установке иницилизации в скетчке командой RF24 radio(48,53);
  • Выводы СЕ и CSN могут быть назначены любые свободные.
  • Питание подключается с соотвествующих выводов платы Arduino: GND и 3.3V.

Пример скетча для клиента

Ниже показан пример скетча беспроводного датчика влажности с возможностью «дергания» пинов для Arduino.

Порядок данных массива:

  • 1 байт номер клиента,
  • 2 байта чтения analogread,
  • 1 байт свободный,
  • 2 байта счетчик ошибок,
  • 4 байта счетчик передач,
  • 4 байта тип float температура,
  • 4 байта тип float влажность.

Используемые датчики: DHT11 или DHT22.

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

/*
Пример работы клиента
*/

#define chclient 1 // номер клиента 1...
#define timeoutper 400 // таймаут запросов от сервера.
#define timesend 300 // интервал отправки данных,для обычных датчиков можно установить время выше.

#include <SPI.h>

#include "DHT.h"

DHT dht;

#include "nRF24L01.h"
#include "RF24.h"

unsigned long time1=0;
unsigned long time2=0;

#define RELE_1 6 //
#define RELE_2 7


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

RF24 radio(9,10);
//

// 0 -прием , 1 -передача
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL,0xF0F0F0F0D2LL};
//

// структура принятых данных.МЕНЯТЬ НЕЛЬЗЯ
typedef struct{
byte identifier;
byte val1;
byte val2;
byte val3;
byte val4;
}
nf0;
nf0 servernf;

// структура отправляемых данных.Изменяемые данные.Размер структуры должен быть не больше 32 байт !
typedef struct{
  byte identifier;// номер передатчика.МЕНЯТЬ НЕЛЬЗЯ

  int Analog;
  boolean test_data;
unsigned int Error_Message; // счетчик ошибок
unsigned long count;// счетчик передач для контроля качества канала
 float temperature_Sensor;// передаём температуру.
 float Humidity_Sensor;// передаём влажность
}
nf1;
nf1 clientnf; 


void setup() {

dht.setup(3); // DHT датчик на пине 3

pinMode(RELE_1, OUTPUT);

pinMode(RELE_2, OUTPUT);

radio.begin();

// выбор скорости
//  radio.setDataRate(RF24_250KBPS);
  radio.setDataRate(RF24_1MBPS);
//  radio.setDataRate(RF24_2MBPS);
   
  radio.setPALevel(RF24_PA_MAX);
  radio.setChannel(100); //тут установка канала
  radio.setCRCLength(RF24_CRC_16);

// radio.setAutoAck(false); // выключить аппаратное потверждение

radio.setRetries(15,15);

radio.openWritingPipe(pipes[1]); // Открываем канал передачи
radio.openReadingPipe(1,pipes[0]); // Открываем канал приема  

clientnf.identifier = chclient;
}

byte errorstate;

void loop() {



//---------------------------------для чтения сенсоров--------------------------
  if ((millis()-time1) >= 1000) {     // обновляем сенсоры раз в секунду (1000млс)
//  тут будут опросы сенсоров

  
clientnf.temperature_Sensor = dht.getTemperature();
clientnf.Humidity_Sensor = dht.getHumidity();

clientnf.Analog=analogRead(0); //пример передачи int данных

time1 = millis();
  }


//************************************************************************************************/
  if ((millis() - time2) >= timesend || errorstate !=0) {
   
if (clientnf.count <= 2147483646)  clientnf.count++;      // счетчик передач для контроля качества канала
else clientnf.count = 0;   
    
  radio.stopListening();
  bool ok = radio.write( &clientnf, sizeof(clientnf) );
  radio.startListening();
 
  unsigned long started_waiting_at = millis();
  bool timeout = false;
  while ( ! radio.available() && ! timeout )
  
  if (millis() - started_waiting_at > timeoutper ) timeout = true;


  if ( timeout ) {
  //  счетчик ошибок
  clientnf.Error_Message++;
  errorstate++; // счетчик ошибок для повтора
  }
  else {
    radio.read( &servernf, sizeof(servernf) );
    errorstate=0;
}
if (errorstate>=3) errorstate=0; // не более 3 попыток для повтора
//************************************************************************************************/


if (servernf.identifier == chclient) { // выполнение команд с сервера,если данные предназначены для этого клиента:

// val1= 10 -значит дергаем пинами, val2 - номер пина, val3 - состояние пина
// не забудте установить режим OUTPUT для нужных пинов.
// nRF-USB write 1 10 7 1 1 - что значит установить на 7 выводе логический уровень 1
    if (servernf.val1==10) digitalWrite(servernf.val2,servernf.val3);
   
// val1= 11 -значит управляем ШИМ пинами, val2 - номер пина, val3 - уровень 0..255.
// не забудте установить режим OUTPUT для нужных пинов.
// ШИМ возможен только на некоторых пинах !!
    if (servernf.val1==11) analogWrite(servernf.val2,servernf.val3);


}

    time2 = millis();      
  }

} // конец loop

Пример скетча для сервера

Расмотрим пример работы для считывания беспроводных модулей на базе nRF24L01 и nRF24LE1 используя Arduino.

Команда для отправки данных на радиомодули:

http://IP_adress/command?chselect=1&val1=11&val2=2&val3=0

Что значит отправить данные val1..val3 на клиента номер 1. Переменная chselect (номер радиомодуля) должна быть всегда первой в строке запроса.

Параметры радиоканала указанные в функции setup должны быть такие же как на клиентах !!

Посмотреть состояние данных можно на страничке

http://IP_adress/state
#include <Ethernet.h>
#include <SPI.h>
#include <Arduino.h>
#include "WebServer.h"
#include "nRF24L01.h"
#include "RF24.h"

#define ch_len 32
byte nfserver[4]; // массив для отправки

// вариант структуры номер 1
typedef struct{
  uint8_t identifier;// номер передатчика.МЕНЯТЬ НЕЛЬЗЯ

  int Analog;
  uint8_t test_data;
//  int Error_Message; // счетчик ошибок
  long count;// счетчик передач для контроля качества канала
#if nofloat
int temperature_Sensor; //передаём температуру.
int Humidity_Sensor;// передаём влажность
#else
 float temperature_Sensor;// передаём температуру.
 float Humidity_Sensor;// передаём влажность
#endif
}
nf1;
// вариант структуры номер 2
typedef struct{
  uint8_t identifier;// номер передатчика.МЕНЯТЬ НЕЛЬЗЯ
  int Analog;
//  int Error_Message; // счетчик ошибок
  long count;// счетчик передач для контроля качества канала
}
nf3;
// задаем каждому клиенту структуру:
nf1 clientnf1; 
nf1 clientnf2;
nf3 clientnf3;


RF24 radio(46,53); // для Arduino MEGA
// RF24 radio(9,8); // Для остальных
//--
// настройка IP адрес ,шлюз,DNS:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xE4, 0xDE, 0x33 }; // MAC-адрес нашего устройства
byte ip[] = { 192, 168, 10, 170 };
byte subnet[] = { 255, 255, 255, 0 };
byte gateway[] = { 192, 168, 10,1 };
byte dns_server[] = { 192, 168, 1, 1};
// ip-адрес удалённого сервера
byte rserver[] = { 192, 168, 1, 154 };

#define VERSION_STRING "0.1"

P(Page_info) = "<html><head><title>controller " VERSION_STRING "</title></head><body>\n";
P(location_info) = "server room";

P(version_info) = VERSION_STRING ". Compile date: " __DATE__ " "__TIME__;

String url = String(25);
int maxLength=25; // Максимальная длинна строки запроса

#define MAX_COMMAND_LEN             (10)
#define MAX_PARAMETER_LEN           (10)
#define COMMAND_TABLE_SIZE          (8)
#define PREFIX ""

WebServer webserver(PREFIX, 80);

#define NAMELEN 32
#define VALUELEN 32

char gCommandBuffer[MAX_COMMAND_LEN + 1];
char gParamBuffer[MAX_PARAMETER_LEN + 1];
long gParamValue;

typedef struct {
  char const    *name;
  void          (*function)(WebServer &server);
} command_t;

command_t const gCommandTable[COMMAND_TABLE_SIZE] = { // таблица команд

  {"cmdnrf",     cmdnrf, },
  {"cmdnrfoff",     cmdnrfoff, },
  {"cmdnrfon",     cmdnrfon, },
  {"chselect",chselect, },
  {"val1",     val1, },
  {"val2",     val2, },
  {"val3",     val3, },
//  {"val4",     val4, },

//  {NULL,      NULL }
};
//------Заполнение массива для отправки--------
void chselect(WebServer &server) {nfserver[0]=gParamValue;} // сохраняем в массив номер клиента
void val1    (WebServer &server) {nfserver[1]=gParamValue;} // id команды
void val2    (WebServer &server) {nfserver[2]=gParamValue;} // данные команды 1
void val3    (WebServer &server) {nfserver[3]=gParamValue;} // данные команды 2
//void val4    (WebServer &server) {nfserver[4]=gParamValue;} // данные команды ещё..


 //**********************************************************************/
void cliProcessCommand(WebServer &server)
{
  int bCommandFound = false;
  int idx;

  gParamValue = strtol(gParamBuffer, NULL, 0);  // Convert the parameter to an integer value. If the parameter is empty, gParamValue becomes 0.
  for (idx = 0; gCommandTable[idx].name != NULL; idx++) {  // Search for the command in the command table until it is found or the end of the table is reached. If the command is found, break out of the loop.
    if (strcmp(gCommandTable[idx].name, gCommandBuffer) == 0) {
      bCommandFound = true;
      break;
    }
  }

  if (bCommandFound == true) {  // Если команда найдена (в массиве команд), то выполняем ее. Если нет - игнорируем
    (*gCommandTable[idx].function)(server);
  }
  else { // Command not found
    server.print("ERROR: Command not found");
  }
}
/*******************************************************************/
/* Загрузка массива для отправки */
void sendcmd (byte ch,byte data1,byte data2,byte data3,byte data4){
/* порядок команд: номер канала,дата1(10 - управление выходами),дата2 (номер выхода) ,дата3 (состояние 1 или 0) */
  
nfserver[0]=ch;
nfserver[1]=data1;
nfserver[2]=data2;
nfserver[3]=data3;
nfserver[4]=data4; 
}

/***************************************************************************************/
/* Обработчики команд */
bool stpin1=0;
bool stpin2=0;

void cmdnrf(WebServer &server) { // пример последовательного вкл/выкл

if (gParamValue==1) {
stpin1=!stpin1;
sendcmd(1,10,0,stpin1,0);
}

if (gParamValue==2) {
stpin2=!stpin2;
sendcmd(2,10,6,stpin2,0);
}


}

void cmdnrfon(WebServer &server) { // команда на включение

if (gParamValue==1) sendcmd(1,10,0,1,0);
if (gParamValue==2) sendcmd(2,10,6,1,0);
}

void cmdnrfoff(WebServer &server) { // команда на выключение

if (gParamValue==1) sendcmd(1,10,0,0,0);
if (gParamValue==2) sendcmd(2,10,6,0,0);
}
/**********************************************************************************************************************
* Разбор запроса
**/
void parsedRequest(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
  URLPARAM_RESULT rc;
  char name[NAMELEN];
  int  name_len;
  char value[VALUELEN];
  int value_len;

  server.httpSuccess();  // this line sends the standard "we're all OK" headers back to the browser

  /* if we're handling a GET or POST, we can output our data here.
     For a HEAD request, we just stop after outputting headers. */
  if (type == WebServer::HEAD)
    return;

  if (strlen(url_tail))
    {
    while (strlen(url_tail)) // Разбор URI на составные части (выборка параметров)
      {
      rc = server.nextURLparam(&url_tail, name, NAMELEN, value, VALUELEN);
      if (rc == URLPARAM_EOS) {
  //      server.printP(Params_end);
      }
       else // Получили параметр (name) и его значение (value)
        {
        // Выполняем команды
        strcpy (gCommandBuffer, name); // параметры (значение)
        strcpy (gParamBuffer, value); // команда
        cliProcessCommand(server);
        }
      }
    }


}

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

void stateRequest(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{

// 1 клиент
server.print("identifier:");
server.print(clientnf1.identifier);

server.print(" Analog:");
server.print(clientnf1.Analog);

server.print(" test_data:");
server.print(clientnf1.test_data);

server.print(" count:");
server.print(clientnf1.count);

//server.print(" Error_Message:");
//server.print(clientnf1.Error_Message);

server.print(" temp:");
server.print(clientnf1.temperature_Sensor);

server.print(" Humidity:");
server.println(clientnf1.Humidity_Sensor);

// 2 клиент
server.print("identifier:");
server.print(clientnf2.identifier);

server.print(" Analog:");
server.print(clientnf2.Analog);

server.print(" test_data:");
server.print(clientnf2.test_data);

server.print(" count:");
server.print(clientnf2.count);

//server.print(" Error_Message:");
//server.print(clientnf2.Error_Message);

server.print(" temp:");
server.print(clientnf2.temperature_Sensor);

server.print(" Humidity:");
server.print(clientnf2.Humidity_Sensor);
}

/*******************************************************************************************************
* Главная страница
**/
void infoRequest(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
  server.printP(Page_info);
  server.print("IP:");
  server.print(Ethernet.localIP());
  server.print("<br>Location:");
  server.printP(location_info);
  
  server.print("<hr><a href='/state'>nRF devices</a>");
  server.print("<hr>Version info: ");
  server.printP(version_info);
 
}

void setup() {
  
    Ethernet.begin(mac, ip, dns_server, gateway, subnet); // Инициализируем Ethernet Shield

  webserver.setDefaultCommand(&infoRequest); // дефолтная страница вывода (информация о контроллере)
  webserver.addCommand("command", &parsedRequest); // команды
  webserver.addCommand("state", &stateRequest); // выдать состояния всех устройств

  webserver.begin();
  
  radio.begin();
  // выбор скорости
//  radio.setDataRate(RF24_250KBPS);
   radio.setDataRate(RF24_1MBPS);
// radio.setDataRate(RF24_2MBPS);
   
  radio.setPALevel(RF24_PA_MAX);
  radio.setChannel(100); //тут установка канала
  radio.setCRCLength(RF24_CRC_16);

// radio.setAutoAck(false); // выключить аппаратное потверждение
// radio.enableDynamicPayloads(); // разрешить Dynamic Payloads
// radio.enableAckPayload();  // разрешить AckPayload

radio.setRetries(15,15);

  radio.openWritingPipe(0xF0F0F0F0E1LL);
  radio.openReadingPipe(1,0xF0F0F0F0D2LL);
  radio.startListening();
}

void loop() {
  
  char buff[64];
  int len = 64;
  webserver.processConnection(buff, &len);  // process incoming connections one at a time forever
  

   byte pipe = 0;
   
   if ( radio.available(&pipe) ) {
  
   char tempdata[ch_len];   
    
    bool done = false;
        
    while (!done)
    {
    
done = radio.read( &tempdata, ch_len );

// смотрим первый байт где находится номер клиента и копируем в структуру для этого клиента.
switch(tempdata[0]){
case 1  :
memcpy(&clientnf1,tempdata, sizeof(clientnf1));
    break;
    
case 2   :
memcpy(&clientnf2,tempdata, sizeof(clientnf2));
    break;
    
case 3   :
memcpy(&clientnf3,tempdata, sizeof(clientnf3));
    break;
    
  }

    }

if (nfserver[0]==tempdata[0]){ // если нам ответил тот клиент,которому отправили команду, то отправляем ему данные
  
   radio.stopListening();
   
   radio.write( &nfserver, sizeof(nfserver) ); 
   
   radio.startListening();
   nfserver[0]=0; // обнуляем номер клиента.
}
  }
  
}

Подключение nRF24L01+ к Raspberry PI

Raspberry PI
SCK11
MOSI9
MISO10
CSN8
CE/SS25

Иструментарий

wi-fi_radiomodul_na_osnove_nrf24l01.txt · Последние изменения: 2015/05/27 17:20 — ansealk
GNU Free Documentation License 1.3
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0 Яндекс.Метрика