19. 3. 2017

Arduino a NRF24L01 - bezdrátová komunikace ve wifi pásmu

Jak na bezdrátovou komunikaci s "wifi" modulem NRF24L01+ pracujícím na frekvenci 2,4Ghz.



Někteří prodejci (hlavně ti z číny) uvádí že to je wifi modul, ale pravda je taková, že se s tímhle k internetu prostě nepřipojíme. Označení wifi má jen kvůli pracovní frekvenci která je stejná jako má náš wifi router. V tomto pásmu funguje i většina dálkových ovladačů RC modelů.

Frekvence 2,4GHz je u nás volně k použití, takže nám odpadá starost s placením licencí, ale na druhou stranu to ssebou nese i nevýhody. Jak už jsem psala, stejnou frekvenci má kde co, takže ve městě se může vyskytnout velké rušení a tím se může snížit efektivní dosah modulů.

Dneska si ukážeme jak posílat různá data bezdrátově s tímhle levným modulem. NRF24L01+ se v číně dá sehnat už za nějakých 15 - 20Kč s integrovanou anténou komunikující běžně na desítky metrů a ve správných podmínkách až na stovky metrů. Anebo NRF24L01+PA+LNA s přídavnou anténou za padesátku s dosahem až 1km.

Stále se prodává i varianta NRF24L01, rozdíl na který jsem narazila je asi jen v rychlosti přenosu. NRF24L01 podporuje pouze 1 a 2 MBPS. NRF24L01+(PA+LNA) podporuje rychlosti 1 a 2MBPS a 250KBPS s o něco větším dosahem.

NRF24L01+
NRF24L01+
Tohle není NRF24L01 i když to mnoho prodejců v číně tvrdí. Je to modul s čipem SE8R01. Není kompatibilní s NRF. Více se o něm píše v tomto blogu.
NRF24L01+PL+LNA

Nejdřív chci upozornit na pár věcí.

1. Vstupní napětí je pouze 1,9 - 3,6V ale vstupy jsou tolerantní na 5V logiku, takže nepotřebujeme převodník logických úrovní.

2. Problémy se stabilitou můžou způsobit proudové špičky, nebo rušení z okolí. Tím se arduino občas sekne a pak se můžeme divit proč to nejde. To se dá vyřešit elektrolitickým kondenzátorem s kapacitou např. 10uF mezi piny VCC a GND modulu NRF. Hodnota kondenzátorů je jen orientační, větší kapacita by neměla být na škodu. Taky se dá sehnat napájecí modul se vším potřebným z číny asi za desetikorunu. Na otestování na stole je OK, ale nedoporučuji ho na větší výkony ani dlouhodobý provoz.
Napájecí deska pro NRF24L01

3. Při vyšších výkonech vysílání je zapotřebí externí napájení 3,3V. Na minimální výkon stačí samo Arduino. Na maximální vysílací výkon se mi Arduino Nano občas seklo i s napájecím kondenzátorem 10uF i když proudový odběr pro vysílání je podle dostupných údajů asi jen 13mA. Za všechno může již zmíněný napájecí modul.

Knihovnu RF24 od uživatele TMRh20 stáhneme v manažeru knihoven, nebo na GitHubu. Testováno s verzí knihovny 1.1.7.

Teď se už vrhneme na zapojení. Pozor, nákres pinů je zeshora dps, ale lišta s piny je směrem dolů. Poprvé jsem si to málem zapojila opačně.

NRF24L01NANO/UNOMEGA
GNDGNDGND
Vcc3,3V3,3V
CE99
CS1010
MOSI1151
MISO1250
SCK1352

Můžeme si otestovat jestli to máme správně zapojené tímto kódem

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

RF24 radio(9,10); // (CE, CS)

void setup(void)
{ 
  Serial.begin(9600);
    printf_begin();
    radio.begin();
    radio.printDetails();
}

void loop(void)
{
}

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

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

Serial monitor:
Pokud máme zapojeno vše jak má být, uvidíme toto..

Něco podobného uvidíme když je modul zapojený špatně.


Pokud je vše vpořádku, můžeme se pustit do komunikace.
Máme zapojené 2x Arduino s NRF.

Funkce je jednoduchá.
Vysílač odešle zprávu, přepne se do přijímacího módu a bude čekat na odpověď. Pokud v zadaný čas odpověď nepřijde, odešle další zprávu a bude zase čekat na odpověď.
Přijímač čeká na zprávu a když ji přijme, tak odpoví. Jednoduché.
Na pin 6 si můžeme připojit piezo pípák a po každé přijaté odpovědi arduino pípne.

Vysílač

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

#define BeepPin 6
#define CE 9
#define CS 10
//MOSI = 11
//MISO = 12
//SCK = 13
//VCC = 3,3V!

RF24 radio(CE, CS);

// nastavíme si adresy a kanál vysílání
const byte vysilac[] = "TX001";
const byte prijimac[] = "RX001";
int kanal = 120;

void setup(void) {
  Serial.begin(9600);
  radio.begin();

  // nastavení rychlosti komunikace
  // možnosti jsouRF24_250KBPS, RF24_1MBPS, RF24_2MBPS
  radio.setDataRate(RF24_1MBPS);
  //kanál na kterém budeme komunikovat
  radio.setChannel(kanal);

  // příjem
  radio.openWritingPipe(prijimac);
  radio.openReadingPipe(1, vysilac);
  radio.startListening();
  
  radio.printDetails();

  // nastavení výkonu nRF modulu,
  // možnosti jsou RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX,
  // pro HIGH a MAX je nutný externí 3,3V zdroj
  radio.setPALevel(RF24_PA_MIN);
  pinMode(BeepPin, OUTPUT);
}

// něco proměnných
char MyCon[40];
char data[40];
int poradi = 1;
bool state = false;
int prvni = 0;
String druhy = "";
long startmillis;
long endmillis;

void loop(void) {
  if (!state) {
    Serial.println("odesilam");
    startmillis = millis();
    //vysílání
    radio.openWritingPipe(vysilac);
    radio.openReadingPipe(0, prijimac);
    radio.stopListening();
    
    sprintf(MyCon, "%i:%s:%i:", poradi, "kanal", kanal);
    radio.write(&MyCon, sizeof(MyCon)); // odeslání zprávy

    Serial.println(MyCon);//***
    poradi++;
    Serial.println("odeslano");
    state = true;
    Serial.println("cekam na odpoved");

    // příjímání
    radio.openWritingPipe(prijimac);
    radio.openReadingPipe(1, vysilac);
    radio.startListening();
  }

  while (state) {
    if (radio.available()) {
      //Serial.println("radio available"); // příjem
      radio.read( &data, sizeof(data));

      prvni = atoi(strtok(data, ":"));
      druhy = strtok(NULL, ":");

      Serial.print("mam odpoved: ");
      Serial.print(prvni); Serial.print(", ");
      Serial.println(druhy);
      //Beep();
      state = false;
    }

    // timeout
    endmillis = millis() - startmillis;
    if ((endmillis >= 1000) && (state)) {
      Serial.println("timeout");
      state = false;
    }
  }

  Serial.print("delka komunikace je ");
  Serial.print(endmillis);
  Serial.println("ms");
  delay(2000);
}
void Beep() {
  int freq = 2800; //frekvence v Hz
  int delka = 60; //délka pípnutí v ms
  tone(BeepPin, freq, delka);
}

Přijímač

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

#define CE 9
#define CS 10
//MOSI = 11
//MISO = 12
//SCK = 13
//VCC = 3,3V!

RF24 radio(CE, CS);

// adresy a kanál
const byte vysilac[] = "TX001";
const byte prijimac[] = "RX001";
int kanal = 120;

void setup(void) {
  Serial.begin(9600);
  radio.begin();

  // nastavení rychlosti komunikace
  // možnosti jsouRF24_250KBPS, RF24_1MBPS, RF24_2MBPS
  radio.setDataRate(RF24_1MBPS);
  //kanál na kterém budeme komunikovat
  radio.setChannel(kanal);

  // příjem
  radio.openWritingPipe(prijimac);
  radio.openReadingPipe(1, vysilac);
  radio.startListening();
  //radio.printDetails();

  // nastavení výkonu nRF modulu,
  // možnosti jsou RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX,
  // pro HIGH a MAX je nutný externí 3,3V zdroj
  radio.setPALevel(RF24_PA_MIN);
  Serial.println("ready");
}

// něco proměnných
char MyCon[40];
char data[40];
bool state = false;
int prvni = 0;
String druhy = "";
int treti = 0;

void loop(void) {
  //Serial.print("state ");Serial.println(state);
  if (!state) {
    if (radio.available()) {
      Serial.println("radio available");
      while (radio.available()) {
        radio.read( &data, sizeof(data) );

        prvni = atoi(strtok(data, ":"));
        String druhy = strtok(NULL, ":");
        int treti = atoi(strtok(NULL, ":"));

        Serial.print("mam zpravu: ");
        Serial.print(prvni);Serial.print(", ");
        Serial.print(druhy);Serial.print(": ");
        Serial.println(treti);
        state = true;
      }
    }
  }

  if (state) {
    // vysílání
    radio.openWritingPipe(vysilac);
    radio.openReadingPipe(0, prijimac);
    radio.stopListening();
    delay(30);// pauza před odesláním 
    //aby se druhý modul stihl přepnout pro příjem

    // odeslat odpoved
    Serial.println("odpovidam");
    sprintf(MyCon, "%i:%s:", prvni, "OK");
    Serial.println(MyCon);
    radio.write(&MyCon, strlen(MyCon));
    state = false;
    Serial.println("odpoved odeslana");

    // a zase přepnout na příjem
    radio.openWritingPipe(prijimac);
    radio.openReadingPipe(1, vysilac);
    radio.startListening();
  }
}

Kanál na kterém moduly komunikují si můžeme nastavit na číslo v rozsahu 0 - 124. Při větších (a záporných) číslech se to chová jako jeden kanál. Kanály nad 108 jsou frekvencí nad většinou domácích wifi routerů.

Adresy pro vysílač a přijímač můžou být jakýkoliv řetězec znaků a čísel. Maximální délku jsem nezkoušela.

Pokud chceme aby nám komunikovalo víc modulů spolu ale nechceme-li aby všechny moduly chytaly zprávy určené jinému, stačí měnit jen kanál před každým vysíláním, nezapomeneme však nastavit stejný kanál i na druhém zařízení jinak se samozřejmně nedomluví.

// TX & Rx adresa pro příjem
radio.openWritingPipe(prijimac);
radio.openReadingPipe(1, vysilac); 
radio.setChannel(123); 
radio.startListening();

// TX & Rx adr pro vysílání
radio.openWritingPipe(vysilac);
radio.openReadingPipe(0, prijimac);
radio.setChannel(123);
radio.stopListening(); 

A na konec sketche ke stažení z google disku. Plus odkaz na můj e-shop, kde si vše potřebné můžete zakoupit

1 komentář:

  1. Díky,
    toto mi pomohlo najít dlooho hledaný problém nespolehlivého přenosu:
    delay(30);// pauza před odesláním
    //aby se druhý modul stihl přepnout pro příjem

    Mirek

    OdpovědětVymazat