NRF24L01 2.4GHz Transceiver Modül

NRF24L01

 

Nordic firmasınca geliştirilen NRF24L01 kablosuz modül, 2.4GHz frekansında kablosuz haberleşme yapmanıza imkan sağlayan düşük güç tüketimine sahip bir modüldür. 2MBps haberleşme hızına sahip olup, SPI arabirimini destekler ve Arduino ile modül arsındaki komut alışverişleri SPI arabirimi üzerinden gerçekleştirilir. nRF24L01 modüller hem alıcı (receiver), hemde verici (transmitter) görevinde kullanılabilir. Hangisinin alıcı, hangisinin verici olacağına  Arduino içerisinde yazdığımız kod ile karar verilmektedir. Projenin ihtiyacına göreçift yönlü haberleşme yapmak için, hem alıcı hem de verici (transceiver) olarak kullanılabilir.

nRF24L01 kablosuz haberleşme modülü, Arduino için “nRF24L01p.h” isimli sürücü kütüphanesine ve bir de “NRF24.h” isimli kütüphaneye sahiptir.

Özellikleri:

  • 2.4GHz bandında yayın yapabilir. Dünya çapında lisans gerektirmeyen 2.4GHz ISM band işletimi (Industrial, Scientific and Medical)
  • 250KBps, 1MBps ve 2MBps gibi hızlarda haberleşme hızı seçilebilir.
  • Gelişmiş ShockBurst™ hızlandırma protokolünü desteklemektedir.
  • Ultra düşük güç tüketimini destekler (ULP solution), aylar yıllar süren bir pil tüketimi vardır.
  • Çalışma Voltajı: 1.9-3.6V
  • IO Portları Çalışma Voltajı:0-3.3V/5V
  • RX/TX modunda 14mA ‘den daha düşük akım çekiş, a sub μA power down mode
  • Verici Sinyal Gücü: +7 dB
  • Alıcı Hassasiyeti ≤ 90dB
  • Haberleşme Mesafesi: Açık Alanda 250m

Modülün SPI (serial Peripheral Interface) haberleşme protokolü kullanarak, arduino ile arasında veri alışverişi yaptığını söylemiştik. Bu nedenle SPI haberleşme protokolü ile haberleşme konusunu blogumuzdaki ilgili yerden tekrar okuduktan sonra devam etmek yararlı olacaktır

PİN  isimleri aşağıdaki gibidir. Modül 3.3v voltaj ile çalışmaktadır, bunun üzerindeki voltajlar modülü bozar.

NRF24L01_PINMAP

 

Arduino UNO ile bağlantı, aşağıdaki gibi yapılıyor. Modül half-duplex üretilmiştir, tranciever modül olduğundan dolayı hem RX(alıcı) hemde TX(verici) görevi görüyor yani aynı anda hem veri alıp hemde veri gönderebiliyor. Hangi modülün alıcı , hangi modülün verici olarak kullanılacağı program içerisinde belirleniyor. Diğer arduino modelleri için bağlantılar farklı olabilir, ancak yine aynı şekilde alıcı/verici olma durumu program ile ayarlanıyor. Modül, 1 ile 25 bayt arasındaki ham data yı aynı anda gönderebiliyor ve veri gönderim hızı yaklaşık, saniyede 1 megabayt’ a kadar olabiliyor. Belirlenmiş olan alıcıya mesaj gönderebiliyor ve yine belirlenmiş olan bir vericiden mesaj alabiliyoruz.  Modül ile mesaj gönderme esnasında, alıcı ve verici adreslerinin belirlenmesi gerekiyor, aynı zamanda göndereceğimiz mesajın kaç bayt olacağını da belirtmemiz gerekiyor. Bazı özel uygulamalarda, alıcı ve verici olma durumları arasında geçişler yapmamız gerekebilir.

NRF24L01 toplam 8 pin içerir. Pin konfigürasyonu ve her bir pinin işlevi aşağıda tanımlanmıştır:

  • Pin#1 GND ‘ye bağlanır ve module GND sağlar.
  • Pin#2 +3,3 volta bağlanır ve modüle güç sağlar. Bu pin VCC olarak dizayn edilmiştir. Modülün çalışması için genellikle 1,9 – 3,6 volt arası bir değer yeterlidir
  • Pin#3 CE olarak dizayn edilmiştir ve TX/RX durumları için çalışma modu seçiminin yapılması için kullanılır
  • Pin#4 CSN olarak dizayn edilmiştir ve modül üzerinde gömülü bulunan SPI haberleşme çipini aktif etmek için kullanılır.
  • Pin#5, modüle SPI clock sinyalinin sağlanması için kullanılır. Bu pin HIGH olduğunda, clock aktif edilmiş olur. Pin LOW olduğunda ise clock pasif hale getirilmiş olur.
  • Pin#6 MOSI olarak dizayn edilmiştir ve modül aracılığıyla alıcı devreye veri gönderilmesi için kullanılır.
  • Pin#7 MISO olarak dizayn edilmiştir ve harici kaynaklardan, bu modül vasıtası ile veri alınmak istendiği zaman, bu pin aktif edilir.
  • Pin#8 IRQ pin olarak dizayn edilmiştir. Kesmeler içindir ancak genelde kullanılmaz

 

Aşağıda  2 adet NRF24L01 ve Arduino devrelerinin birbirleri ile haberleşmesini göstereceğiz. Bunun için malzemelerimizden 2’şer tane olması gerekmekte. Aşağıdaki programlar için kullanılan RF24.h kütüphanesinin maniacBug versiyonu indirilebilir. Daha ileriki konularda anlatılacak programlar için aynı kütüphane nin farklı bir versiyonu kullanılacak. Onu da ayrıca belirteceğim.

NRF24L01 ve ARDUINO (VERİCİ Devresi)

 

NRF24L01 ile Arduino’nun bağlantılarını aşağıdaki şekilde gösterildiği gibi yapın:

NRF24L01-with-Arduino-Response-Timed-Out2

 

Toplam 7 pin bağlantısı yapılacak. Yani 8. pin olan IRQ’yu herhangi bir yere bağlamayacağız.

Bir sonraki adımda yapmamız gereken, Arduinonun RF24 kütüphanesini indirmek ve Arduino software deki library klasörüne koymak.

Devremizi kurduk ve kütüphanemizi de hazırladıktan sonra aşağıdaki kodu , transmitter (verici) olarak kullanacağımız Arduino’ya yükleyebiliriz.

#include <SPI.h>    

#include "nRF24L01.h"    

#include "RF24.h"     

RF24 verici(9,10);    

 const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };    

unsigned long gidenMesaj = 1;    

void setup()    {    

Serial.begin(9600);     

verici.begin();    

verici.setRetries(15,15);    

verici.openReadingPipe(1,pipes[1]);    

verici.startListening();    

verici.printDetails();    

verici.openWritingPipe(pipes[0]);    

verici.openReadingPipe(1,pipes[1]);    

verici.stopListening();   

 }     

void loop(void)    {    

verici.stopListening();     

verici.write( & gidenMesaj, sizeof(unsigned long) );     

verici.startListening();     

delay(1000);    

}

Yukarıdaki kodda tanımlanmış olan “gidenMesaj” isimli değişken NRF24L01 ile yollanacak olan mesajı içeriyor. Bu değişkenin içeriğini 3 yaparak, alıcıya 3 yollayabirisiniz.

NRF24L01 ve ARDUINO (ALICI Devresi)

NRF24L01 ile Arduino’nun bağlantılarınıverici devresi ile aynı şekilde yapın NRF24L01-with-Arduino-Response-Timed-Out2

Bağlantılar bittikten sonra, aşağıdaki kodu arduinoya yükleyin, iki devreye de enerji verip çalıştırdığınızda, alıcı devresini serial monitörde açın. Ekranda göreceğiniz mesaj, verici devresinden gelen mesajdır.

#include <SPI.h>    

#include "nRF24L01.h"    

#include "RF24.h"     

RF24 alici(9,10);     

const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };     

typedef enum { role_ping_out = 1, role_pong_back } role_e;     

const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"};     

role_e role = role_pong_back;     

void setup(void)    {    

Serial.begin(9600);    

alici.begin();    

alici.setRetries(15,15);    

alici.openReadingPipe(1,pipes[1]);    

alici.startListening();    

alici.printDetails();    

alici.openWritingPipe(pipes[1]);   

 alici.openReadingPipe(1,pipes[0]);    

alici.startListening();   

 }     

void loop(void)    {     

if (alici.available() )    {    

unsigned long data = 0;    

alici.read( &data, sizeof(unsigned long) );    

Serial.println(data);     

delay(20);    

}    

}

 

Not: Tüm kablo bağlantılarının hatasız bir şekilde yapılmasına rağmen, bazen modüller birbiri arasındaki haberleşmeyi sağlayamamaktadır. Böyle bir durumda modüllerin VCC (3.3V) ve GND pinleri arasına 3.3 uF ile 10 uF arasında bir kapasite koyarak tekrar deneyin. Eğer NRF24L01+  (yeni model) modül kullanıyorsanız 100uF değerinde bir kondansatör koyarak deneyin. Çünkü yeni modüller çalışmak için, eski modüllere göre daha fazla akım çekiyor.

——————————————————————————————————-

İPUÇLARI:

  1.  Bağlantılar karıştırılabiliyor her kütüphanede farklı bağlantılar oluyor buna  dikkat edin.
  2.  Kartınızın 3.3v çıkışı stabil olmayabiliyor modül üzerinde vcc ve gnd arasına 10  uf kondansatör lehimleyin.
  3.  Jumperlar parazit yapabiliyor. Mümkünse bağlantıları lehimleyin.
  4.  nRF24’ ler SPI haberleşme protokolünü kullandıkları için, SCK, MISO ve MOSI  bağlatıları için mutlaka Arduino’nun 13,12 ve 11 nolu pinleri kullanılmalı. CSN  ve CE pinleri için Arduinonun diğer pinleri de kullanılabilir ama genelde 9. ve  10. pinler kullanılıyor. Ancak 9 ve 10 nolu pinlerdem başka pin kullanacaksanız,  Arduinonun SPI master olarak davranmasını sağlamak için 10. pini mutlaka  OUTPUT olarak set etmelisiniz.
  5.  Arduinoya program yüklediğinizde bazı durumlarda nRF24 resetlenmemiş  olabiliyor. Bu nedenle, yeni program yükledikten sonra haberleşme ile ilgili bir  sorun yaşarsanız, arduinoyu tamamen enerjisiz kalacak şekilde USB kablosunu  veya güç besleme kablosundan ayırın 10-15 sn. bekletin..
  6.  2’den fazla haberleşme modülü kullanacağınız zaman, Modül haberleşme hat  adreslerini belirlerken Veri hatları bölümlü başlıkta yazılanları iyi okuyun.
  7.  RF24.h isimli kütüphane dosyasını açıp içine bakın. Fonksiyonların ne işe  yaradığını anlamaya çalışın.
  8.  Arduino UNO kartlarının üzerindeki ICSP konnektördeki SPI pinleri bazı dijital  I/O pinlerle paralel bağlantılıdır. Modemi kullanırken, bu dijital pinleri  kullanamayacağınızı unutmayın.
  • MOSI,  digital pin 11’e bağlıdır
  • MISO, digital pin 12 ‘ye bağlıdır
  • SCK, digital pin 13 ‘e bağlıdır
  • SCN, digital pin 10’a bağlıdır.

——————————————————————————————————-

Aşağıdaki tanımlamalar Arduino forumundaki   http://forum.arduino.cc/index.php?topic=421081  adresinde anlatılanların çevirileridir. Yabancı dili olanlar forumdan konuyu okuyabilirler. Umarım konuyla ilgili bilgi edinmek isteyenlere yardımcı olabilirim.

RF24 kütüphanesi TMRh20 versiyonu

Bu konu anlatımında RF24 kütüphanesinin TMRh20 versiyonunu kullanmaktadır. Aynı kütüphanenin ManiacBug versiyonundaki bazı hatalar, TMRh20 versiyonunda düzeltilmiş haldedir. Eğer daha önce RF24 kütüphanesi indirdiyseniz, burada anlatılan konulardaki programları çalıştıramayabilirsiniz. Bu konuyu dikkate almalısınız.

nRF24L01+ modul pin bağlantıları

 Daha önce anlatılan konularda da aynı pin bağlantı şeklini kullanmıştık. Pin bağlantıları, arduinoya da daha önce nasıl bağladıysak yine aynı şekilde olacak.

Wifi ve Bloutooth modülleri gibi, nRF24L01 modüller 2.4GHz bandında yayın yapar. Kesin frekans, seçilen kanal ile kararlaştırılır. Haberleşmenin yapılabilmesi için Alıcı ve Verici modüller aynı kanalı kullanmak zorundadırlar. RF24 kütüphaesi için default ayarlı olan kanal 76’dır.

Verici modül bir mesaj gönderdiğinde, anyı kanalı dinleyen tüm alıcı modüller gönderilen mesajı alırlar. Verici modül mesaj ile birlikte adres bilgisi (kanal) de gönderir ve alıcı modül bu adrese sahip değilse, gönderilen mesajı göz ardı eder, tanımaz. Adres, 5 baytlık bir sayıdır.

Radyo Paraziti

2 veya daha fazla Vericinin aynı anda aynı kanalı kullanması birbirlerine parazit oluştracağından, gönderilen mesaj Alıcı tarafından alınamayabilir. Bu yüzden nRF24L01, içinde mevcut bulunan 6 kanaldan aynı anda mesaj alamaz.

Bir diğer husus da, yakındaki bir Wifi sistemi aynı frekansta aynı anda veri iletiyorsa, bizim yolladığımız mesajları da bozacaktır ve yine veriler parazite uğrayacağından alınamayabilir.

Bununla birlikte, her bir iletim çok kısadır (birkaç milisaniye), Vericinizdeki iletinin başka bir şeyle  çakışması çok da olası değildir.

Veri Hatları (Yolları)

Multiceiver Özelliği

Multiceiver, kendi özel adresleri olan 6 paralel veri hattı seti içeren RX modunda kullanılan bir özelliktir.

Veri hattı, fiziksel RF kanalı içerisinde lojik (mantıksal) bir kanaldır. nRF24L01’de her veri hattının kendi fiziksel adres kod çözümü vardır.

nrf24L01 pipes1

PRX (birincil alıcı) olarak yapılandırılmış nRF24L01, Şekil 10’da gösterildiği gibi bir frekans kanalında altı farklı veri hattına gönderilen verileri alabilir.

Her veri hattının kendi özel adresi vardır ve bireysel davranış için yapılandırılabilir.

PTX olarak yapılandırılan altı adete kadar nRF24L01, PRX olarak yapılandırılmış bir nRF24L01 ile iletişim kurabilir. Tüm veri hattı  adresleri aynı anda aranır, doğru adres bilgisi olan kanala veri aktarılır. Aynı anda yalnızca bir veri hattı, bir paket alabilir. Tüm veri hatları, Geliştirilmiş ShockBurst ™ işlevselliğini gerçekleştirebilir.

Veri hatlarının herhangibirinden bir paket alındığında ve veri hattı, alındı bilgisi oluşturmak için set edildiğinde; nRF24L01, paketin alındığı veri hattı adresine eşit bir adresle bir onay bilgisi oluşturacaktır.

Bazı yapılandırma ayarları tüm veri hatlarında ortaktır ve bazıları bireyseldir.

Aşağıdaki ayarlar tüm veri hatlarında ortaktır:

 

 

  • CRC etkin / devre dışı ( ESB etkinleştirildiğinde, CRC daima etkin)
  • CRC kodlama şeması
  • RX adres genişliği
  • Frekans kanalı
  • RF veri hızı
  • LNA kazancı
  • RF çıkış gücü

Veri hatları, EN_RXADDR kaydedicisindeki bitlerle etkinleştirilir. Varsayılan olarak, yalnızca veri hattı 0 ve 1 etkinleştirilmiştir. Her bir veri hattı adresi RX_ADDR_PX kaydedicisnde yapılandırılmıştır.

Not: Veri hatlarının hiçbirinin aynı adrese sahip olmadığından emin olun.

Her hat, 5 bayt’a kadar konfigüre edilebilir adrese sahip olabilir. Veri hattı 0 benzersiz bir 5 bayt adrese sahiptir. Veri hatları 1-5, en önemli 4 adres baytlarını ortak olarak kullanır ve 5. bayt ile de bu kanallara adres ataması yapılır. LSByte, tüm 6 hat için birbirinden farklı olmalıdır. Şekil 11, 0-5 nolu veri hatlarının nasıl adreslendiğine dair bir örnektir

nrf24L01 pipes2

Multiceiver ve Enhanced ShockBurst ™ kullanan PRX, birden fazla PTX’ den paket alır. PRX’den gelen ACK paketinin doğru PTX’e iletildiğinden emin olmak için, PRX, paketin alındığı veri hattı adresini alır ve ACK paketini iletirken onu TX adresi olarak kullanır.

PTX cihazında veri hattı 0, alındı bildirimini almak için kullanılır. Bu nedenle (alındı bildirimini alabilmek için) PTX cihazının veri hattı 0 için alma adresi, gönderim adresine eşit olmalıdır.

Şekil 12, adres yapılandırmasının PRX ve PTX için nasıl olabileceğinin bir örneğidir. PRX’te, hat  adresi olarak tanımlanan RX_ADDR_Pn’ nin benzersiz olması gerekir. PTX’ de TX_ADDR, RX_ADDR_P0 ile aynı olmalı ve belirlenen hat için, hat adresi olmalıdır.

nrf24L01 pipes3

 

Adresi algılanan bir veri hattı bir veri paketini komple alıncaya kadar, diğer hiçbir veri hattı veri alamaz.

Birden çok PTX, bir PRX’e veri göndermeye çalıştığında, ARD, otomatik yeniden iletiyi çarpıtmak için kullanılabilir; böylece yalnızca bir kez birbirlerini engellemiş olurlar.

 

NRF24L01 + modülleri 6 “hat” ın herhangi birini veya tümünü dinleyebilir. Maalesef bazı RF24 demo programlarında, adresleri tutmak için kullanılan değişkene, (hatlar ve adresler tamamen farklı olsalar bile) “pipe” adı verilir. Bu derste 1 adet alıcı (PRX) ve 1 adet verici (PTX) kullanılacağından dolayı ve dersin amacı doğrultusunda yalnızca bir hattı (hat 1 – pipe 1) dinlemek için kullanacağım. Hat 0 (pipe 0)’ın yalnızca yazma hattı olduğunu unutmayın. Hat 0 da dinlenilebilir, ancak bunu yalnızca yazma için bırakmak daha kolaydır.

6 veri hattı, nRF24’ün 6 farklı adresli 6 cihazdan gelen mesajları dinlemesine olanak tanır. Ancak bu cihazlar verileri aynı anda iletemezler.

Veri Paketleri

NRF24L01 + modülleri, tek bir mesajda maksimum 32 bayt iletebilir. Daha fazla göndermeniz gerekiyorsa, birkaç ayrı mesaja bölmeniz gerekecektir. Bu ders için 32 bayttan fazla gönderilmeyecektir.

 

İki çalışma modu vardır

{A} varsayılan olarak 32 bayt sabit bir yük boyutu olan ve boyutu setPayloadSize() ile değiştirilebilen

{B} enableDynamicPayloads () ile seçilen dinamik bir yük yükleme modu.

 

Dinamik yük yükleme modu, ackPayload özelliğini seçtiğinizde otomatik olarak uygulanır (bkz. Örnek 2). GetDynamicPayloadSize () ile yükün boyutunu kontrol edebilirsiniz.
Dinamik yükleri kullanırken, alınan tüm baytları okuduğunuzdan veya iletişimin bozululup bozulmadığından emin olmanız gerekir.

Veri Doğrulama

NRF24’ler alınan verilerin gönderilen veriyle eşleşip eşleşmediğini saptamak için gelişmiş sistemleri otomatik olarak içerir. Veriler doğru şekilde alınmazsa, Alıcı mevcut verileri göstermeyecek ( available()  ) ve bir bildirim göndermeyecektir. Bunun anlamı şudur, Verici iletimin başarısız olduğunu düşünecektir. Örnek programlarda Verici’ nin  gönderimden vazgeçmeden önce iletimini otomatik olarak en az 5 kez tekrarlamasını sağladığını göreceksiniz. (Maksimum 15’dir).

Basit tek yönlü iletim

Aşağıdaki program çifti, TX’den RX’e “Mesaj N” şeklinde basit bir mesaj gönderir. Burada N, 0’dan 9’a artan bir sayıdır ve böylece, ardışık mesajların gönderildiğini ve alındığını görebilirsiniz.

SimpleTx.ino

 // SimpleTx - the master or the transmitter
 
 #include <SPI.h>
 #include <nRF24L01.h>
 #include <RF24.h>
 
 
 #define CE_PIN   9
 #define CSN_PIN 10
 
 const byte slaveAddress[5] = {'R','x','A','A','A'};
 
 
 RF24 radio(CE_PIN, CSN_PIN); // Create a Radio
 
 char dataToSend[10] = "Message 0";
 char txNum = '0';
 
 
 unsigned long currentMillis;
 unsigned long prevMillis;
 unsigned long txIntervalMillis = 1000; // send once per second
 
 
 void setup() {
 
     Serial.begin(9600);
 
     Serial.println("SimpleTx Starting");
 
     radio.begin();
     radio.setDataRate( RF24_250KBPS );
     radio.setRetries(3,5); // delay, count
     radio.openWritingPipe(slaveAddress);
 }
 
 //====================
 
 void loop() {
     currentMillis = millis();
     if (currentMillis - prevMillis >= txIntervalMillis) {
         send();
         prevMillis = millis();
     }
 }
 
 //====================
 
 void send() {
 
     bool rslt;
     rslt = radio.write( &dataToSend, sizeof(dataToSend) );
         // Always use sizeof() as it gives the size as the number of bytes.
         // For example if dataToSend was an int sizeof() would correctly return 2
 
     Serial.print("Data Sent ");
     Serial.print(dataToSend);
     if (rslt) {
         Serial.println("  Acknowledge received");
         updateMessage();
     }
     else {
         Serial.println("  Tx failed");
     }
 }
 
 //================
 
 void updateMessage() {
         // so you can see that new data is being sent
     txNum += 1;
     if (txNum > '9') {
         txNum = '0';
     }
     dataToSend[8] = txNum;
 }
 
 

 

SimpleRx.ino

 // SimpleRx - the slave or the receiver
 
 #include <SPI.h>
 #include <nRF24L01.h>
 #include <RF24.h>
 
 #define CE_PIN   9
 #define CSN_PIN 10
 
 const byte thisSlaveAddress[5] = {'R','x','A','A','A'};
 
 RF24 radio(CE_PIN, CSN_PIN);
 
 char dataReceived[10]; // this must match dataToSend in the TX
 bool newData = false;
 
 //===========
 
 void setup() {
 
     Serial.begin(9600);
 
     Serial.println("SimpleRx Starting");
     radio.begin();
     radio.setDataRate( RF24_250KBPS );
     radio.openReadingPipe(1, thisSlaveAddress);
     radio.startListening();
 }
 
 //=============
 
 void loop() {
     getData();
     showData();
 }
 
 //==============
 
 void getData() {
     if ( radio.available() ) {
         radio.read( &dataReceived, sizeof(dataReceived) );
         newData = true;
     }
 }
 
 void showData() {
     if (newData == true) {
         Serial.print("Data received ");
         Serial.println(dataReceived);
         newData = false;
     }
 }
 

İki yönlü iletim

İki yönlü iletimde, Master ve Slave olarak belirlenen cihazların rollerinin bir süreliğine değiştirilmesi mantığına dayanır. Eğer Slave, verileri Master’a geri göndermek zorundaysa, iki cihazın rollerini takas etmesi gerekmektedir. Bununla birlikte, “Master” in da Slave gibi, bir adrese ihtiyacı olacaktır. Zamanlama konusu da dikkate alınmalıdır – örneğin; Master, Slave’den gelecek olan yanıt için Slave’i ne kadar uzun süre dinlemeye devam edecek? Ve eğer Slave dinlemek yerine, yazmak isterse? Master’dan gelecek olan  bir ileti kaçırılabilir.

AckPayload konseptini kullanarak iki yönlü iletim

Rolleri değiştirmenin komplikasyonları, ackPayload kavramını kullanarak tamamen önlenebilir. Fikir şöyledir; Slave olarak belirlenen cihaz, ana cihazdan bir mesaj almadan ÖNCE ackPayload buffer’a veri koyar ve daha sonra, ackPayload buffer’daki veriler normal onaylama sürecinin (alındı bildirimi gönderilmesi) bir parçası olarak otomatik olarak gönderilir ve kodlamada Verici ile Alıcı cihazların rollerini takas etmesine gerek kalmaz.

AckPayload konsepti sadece slave’den master’a aktarılan veri miktarı 32 byte veya daha az olduğu zaman kullanılabilir. Aynı zamanda Slave tarafından gönderilen veriler her zaman aldığı verilerin bir adım gerisindedir. Örneğin, ackPayload, onaylama için aldığı mesajdaki verileri hesaba katamaz.

 

ackPayload örneği

Bu örnekte, Slave  bir çift sayıyı master’a geri gönderir.

SimpleTxAckPayload.ino

 // SimpleTxAckPayload - the master or the transmitter
 
 #include <SPI.h>
 #include <nRF24L01.h>
 #include <RF24.h>
 
 
 #define CE_PIN   9
 #define CSN_PIN 10
 
 const byte slaveAddress[5] = {'R','x','A','A','A'};
 
 RF24 radio(CE_PIN, CSN_PIN); // Create a Radio
 
 char dataToSend[10] = "Message 0";
 char txNum = '0';
 int ackData[2] = {-1, -1}; // to hold the two values coming from the slave
 bool newData = false;
 
 unsigned long currentMillis;
 unsigned long prevMillis;
 unsigned long txIntervalMillis = 1000; // send once per second
 
 //===============
 
 void setup() {
 
     Serial.begin(9600);
     Serial.println(F("Source File /mnt/sdb1/SGT-Prog/Arduino/ForumDemos/nRF24Tutorial/SimpleTxAckPayload.ino"));
     Serial.println("SimpleTxAckPayload Starting");
 
     radio.begin();
     radio.setDataRate( RF24_250KBPS );
 
     radio.enableAckPayload();
 
     radio.setRetries(3,5); // delay, count
     radio.openWritingPipe(slaveAddress);
 }
 
 //=============
 
 void loop() {
 
     currentMillis = millis();
     if (currentMillis - prevMillis >= txIntervalMillis) {
         send();
     }
     showData();
 }
 
 //================
 
 void send() {
 
     bool rslt;
     rslt = radio.write( &dataToSend, sizeof(dataToSend) );
         // Always use sizeof() as it gives the size as the number of bytes.
         // For example if dataToSend was an int sizeof() would correctly return 2
 
     Serial.print("Data Sent ");
     Serial.print(dataToSend);
     if (rslt) {
         if ( radio.isAckPayloadAvailable() ) {
             radio.read(&ackData, sizeof(ackData));
             newData = true;
         }
         else {
             Serial.println("  Acknowledge but no data ");
         }
         updateMessage();
     }
     else {
         Serial.println("  Tx failed");
     }
 
     prevMillis = millis();
  }
 
 
 //=================
 
 void showData() {
     if (newData == true) {
         Serial.print("  Acknowledge data ");
         Serial.print(ackData[0]);
         Serial.print(", ");
         Serial.println(ackData[1]);
         Serial.println();
         newData = false;
     }
 }
 
 //================
 
 void updateMessage() {
         // so you can see that new data is being sent
     txNum += 1;
     if (txNum > '9') {
         txNum = '0';
     }
     dataToSend[8] = txNum;
 }
 
 

 

SimpleRxAckPayload.ino

 // SimpleRx - the slave or the receiver
 
 #include <SPI.h>
 #include <nRF24L01.h>
 #include <RF24.h>
 
 #define CE_PIN   9
 #define CSN_PIN 10
 
 const byte thisSlaveAddress[5] = {'R','x','A','A','A'};
 
 RF24 radio(CE_PIN, CSN_PIN);
 
 char dataReceived[10]; // this must match dataToSend in the TX
 int ackData[2] = {109, -4000}; // the two values to be sent to the master
 bool newData = false;
 
 //==============
 
 void setup() {
 
     Serial.begin(9600);
 
     Serial.println("SimpleRxAckPayload Starting");
     radio.begin();
     radio.setDataRate( RF24_250KBPS );
     radio.openReadingPipe(1, thisSlaveAddress);
 
     radio.enableAckPayload();
     radio.writeAckPayload(1, &ackData, sizeof(ackData)); // pre-load data
 
     radio.startListening();
 }
 
 //==========
 
 void loop() {
     getData();
     showData();
 }
 
 //============
 
 void getData() {
     if ( radio.available() ) {
         radio.read( &dataReceived, sizeof(dataReceived) );
         updateReplyData();
         newData = true;
     }
 }
 
 //================
 
 void showData() {
     if (newData == true) {
         Serial.print("Data received ");
         Serial.println(dataReceived);
         Serial.print(" ackPayload sent ");
         Serial.print(ackData[0]);
         Serial.print(", ");
         Serial.println(ackData[1]);
         newData = false;
     }
 }
 
 //================
 
 void updateReplyData() {
     ackData[0] -= 1;
     ackData[1] -= 1;
     if (ackData[0] < 100) {
         ackData[0] = 109;
     }
     if (ackData[1] < -4009) {
         ackData[1] = -4000;
     }
     radio.writeAckPayload(1, &ackData, sizeof(ackData)); // load the payload for the next time
 }
 

 

Rolleri değiştirerek iki yönlü iletim

Bu yaklaşım ihtiyaç duymak için bir neden bulamadım, ancak bunu kullanmak isteyen başkaları için yararlı olması amacıyla bu konsepti de göstereceğim.

Bu örnekte, iki nRF24 (Master ve Slave) zamanlarının çoğunda dinleme yaparlar ve yalnızca mesaj göndermek için gereken minimum sürede konuşmaya geçer. AckPayload örneğinde olduğu gibi, Slave ana cihazdan gelen bir mesaja yanıt olarak yalnızca bir mesaj gönderir.

MasterSwapRoles.ino

 // MasterSwapRoles
 
 #include <SPI.h>
 #include <nRF24L01.h>
 #include <RF24.h>
 
 
 #define CE_PIN   9
 #define CSN_PIN 10
 
 const byte slaveAddress[5] = {'R','x','A','A','A'};
 const byte masterAddress[5] = {'T','X','a','a','a'};
 
 
 RF24 radio(CE_PIN, CSN_PIN); // Create a Radio
 
 char dataToSend[10] = "Message 0";
 char txNum = '0';
 int dataReceived[2]; // to hold the data from the slave - must match replyData[] in the slave
 bool newData = false;
 
 unsigned long currentMillis;
 unsigned long prevMillis;
 unsigned long txIntervalMillis = 1000; // send once per second
 
 //============
 
 void setup() {
 
     Serial.begin(9600);
 
     Serial.println("MasterSwapRoles Starting");
 
     radio.begin();
     radio.setDataRate( RF24_250KBPS );
 
     radio.openWritingPipe(slaveAddress);
     radio.openReadingPipe(1, masterAddress);
 
     radio.setRetries(3,5); // delay, count
     send(); // to get things started
     prevMillis = millis(); // set clock
 }
 
 //=============
 
 void loop() {
     currentMillis = millis();
     if (currentMillis - prevMillis >= txIntervalMillis) {
         send();
         prevMillis = millis();
     }
     getData();
     showData();
 }
 
 //====================
 
 void send() {
 
         radio.stopListening();
             bool rslt;
             rslt = radio.write( &dataToSend, sizeof(dataToSend) );
         radio.startListening();
         Serial.print("Data Sent ");
         Serial.print(dataToSend);
         if (rslt) {
             Serial.println("  Acknowledge received");
             updateMessage();
         }
         else {
             Serial.println("  Tx failed");
         }
 }
 
 //================
 
 void getData() {
 
     if ( radio.available() ) {
         radio.read( &dataReceived, sizeof(dataReceived) );
         newData = true;
     }
 }
 
 //================
 
 void showData() {
     if (newData == true) {
         Serial.print("Data received ");
         Serial.print(dataReceived[0]);
         Serial.print(", ");
         Serial.println(dataReceived[1]);
         Serial.println();
         newData = false;
     }
 }
 
 //================
 
 void updateMessage() {
         // so you can see that new data is being sent
     txNum += 1;
     if (txNum > '9') {
         txNum = '0';
     }
     dataToSend[8] = txNum;
 }
 
 

 

SlaveSwapRoles.ino

 // SlaveSwapRoles
 
 #include <SPI.h>
 #include <nRF24L01.h>
 #include <RF24.h>
 
 
 #define CE_PIN   9
 #define CSN_PIN 10
 
 const byte slaveAddress[5] = {'R','x','A','A','A'};
 const byte masterAddress[5] = {'T','X','a','a','a'};
 
 RF24 radio(CE_PIN, CSN_PIN); // Create a Radio
 
 char dataReceived[10]; // must match dataToSend in master
 int replyData[2] = {109, -4000}; // the two values to be sent to the master
 bool newData = false;
 
 unsigned long currentMillis;
 unsigned long prevMillis;
 unsigned long txIntervalMillis = 1000; // send once per second
 
 
 void setup() {
 
     Serial.begin(9600);
 
     Serial.println("SlaveSwapRoles Starting");
 
     radio.begin();
     radio.setDataRate( RF24_250KBPS );
 
     radio.openWritingPipe(masterAddress); // NB these are swapped compared to the master
     radio.openReadingPipe(1, slaveAddress);
 
     radio.setRetries(3,5); // delay, count
     radio.startListening();
 
 }
 
 //====================
 
 void loop() {
     getData();
     showData();
     send();
 }
 
 //====================
 
 void send() {
     if (newData == true) {
         radio.stopListening();
             bool rslt;
             rslt = radio.write( &replyData, sizeof(replyData) );
         radio.startListening();
 
         Serial.print("Reply Sent ");
         Serial.print(replyData[0]);
         Serial.print(", ");
         Serial.println(replyData[1]);
 
         if (rslt) {
             Serial.println("Acknowledge Received");
             updateReplyData();
         }
         else {
             Serial.println("Tx failed");
         }
         Serial.println();
         newData = false;
     }
 }
 
 //================
 
 void getData() {
 
     if ( radio.available() ) {
         radio.read( &dataReceived, sizeof(dataReceived) );
         newData = true;
     }
 }
 
 //================
 
 void showData() {
     if (newData == true) {
         Serial.print("Data received ");
         Serial.println(dataReceived);
     }
 }
 
 //================
 
 void updateReplyData() {
     replyData[0] -= 1;
     replyData[1] -= 1;
     if (replyData[0] < 100) {
         replyData[0] = 109;
     }
     if (replyData[1] < -4009) {
         replyData[1] = -4000;
     }
 }
 

 

Konu ile ilgili başka bir örnek

NRF24L01_arduino2

Tüm bağlantılar yapıldıktan sonra, uygulamasını yapacağımız ilk program şöyle olacak.

Verici cihazımız vasıtası ile yolladığımız “Merhaba Dunya” stringini, Alıcı cihazın bağlı bulunduğu bilgisayarın Serial Monitöründe göreceğiz.

Bu uygulamada RF24 kütüphanesinin TMRh20 versiyon olanını kullanacağız. Kütüphaneyi https://github.com/TMRh20/RF24 adresinden indirebilirsiniz.

Verici Programı:

#include <SPI.h>

#include <nRF24L01.h>

#include <RF24.h> 

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

//const uint64_t rxAddr = 0xF0F0F0F0E1LL; 

const byte rxAddr[6] = "00001"; 

void setup(){  

radio.begin();  

radio.setRetries(15, 15);  

radio.openWritingPipe(rxAddr);   

 radio.stopListening();

} 

void loop(){  

const char text[] = "Merhaba Dunya";  

radio.write(&text, sizeof(text));    

delay(1000);

}

 

Programımızın en başında kütüphane dosyalarını ekleyerek, gerekli bilgilendirmeyi yapıyoruz

#include <SPI.h>  //nRF24L01 için kullanacağımız haberleşme protokünü belirtiyoruz.

#include <nRF24L01.h>   //nRF24L01’in sürücüsünü belirtiyoruz.

#include <RF24.h>  //nRF24L01’i kontrol-kumanda etmemiz için gerekli kütüphane

Bir sonraki adımda aşağıdaki gibi, “radio” isimli bir nesne oluşturuyoruz. Ve bu nesnenin CE ve CSN ayaklarının bağlanacağı pinleri belirtiyoruz. Bu oluşturdğumuz nesne, Arduinomuza bağlı olan nRF24L01 modülü temsil ediyor. Yani kısacası, modülümüze bir isim vermiş oluyoruz, daha sonra da kütüphane komutlarını kullanırken her bir komutun başına bu nesnen ismini yazıyoruz. Bunun amacı, kütüphane komutlarının (fonksiyonlarının) hangi nesne ile ilgili işlem yapacağını belirtmiş olmaktır.

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

Daha sonraki adımda, aşağıdaki gibi “rxAddr” adında, bir global scope seviyesine sahip dizi değişkeni oluşturuyoruz. Bu değişken içine sakladığımız değer, Arduinodan veri alacak olan nRF24L01 modülün adresini gösteriyor. Adresi değiştirmek isterseniz, yine 5 karakterden oluşan başka bir şey de yazabilirsiniz. Adresin 5 karakter olmasının nedenini nRF24L01 modülün NORDIC firması tarafından hazırlanmış olan datasheet sayfasında Data Pipes konusu altında tüm detayları ile bulabilirsiniz. Sayfanın yukarılarında da bu konuyla ilgili bir miktar bilgi verilmişti.

Burada başka bir detayı daha belirtmek istiyorum. Adresleme için farklı veri tanımlama şakileri olabilir, ancak aşağıdaki gibi bir veri tanımlamasından bahsedecek olursak 6 adetlik bir dizi tanımlaması yapılmasına rağmen içinde 5 birimlik bir veri olmasının nedeni nedir? Öncelikle aşağıdaki veri tanımlaması şeklinde, içerik bir string olarak tanımlanmıştır. Bu tip veri tanımlaması yapıldığında, string içindeki veriler, karakter katarları şeklinde peşpeşe gönderilir ve katarın bittiğini belirtmek için de otomatik olarak \n ( null) karakteri gönderilir. Bu nedenle string array tipi tanımlamalar yaparken, içerikten 1 fazla array elemanı tanımlanmalıdır. Konu ile ilgili daha fazla detay için String Array konusunda bir araştırma yapmalısınız.

const byte rxAddr[6] = “00001”;

Daha sonraki adımda setup() fonksiyonu içerisinde bulunan tanımlamaları yapıyoruz.

Aşağıdaki fonksiyon ile, haberleşme yapacak olduğumuz nRF24L01 modülün ismini belirterek begin() fonksiyonunu çalıştırıyoruz.

radio.begin();

bu fonksiyon ile, radio ismi ile tanımladığımız nRF24L01 modülümüzü aktif hale getiriyoruz,

Daha sonraki adımda, aşağıdaki fonksiyonu tanımlıyoruz.

radio.setRetries(15, 15); // (delay, count)

bu fonksiyonda iki adet parametre belirtiyoruz.

delay = her tekrar deneme arasında ne kadar süre bekleneceğini belirtmek için kullanılıyor. Bu süre 250us’nin katları şeklinde belirleniyor. 0 ile 15 arasında bir değer verilebiliyor, 0 = 250us ve 15 = 4000us’ yi temsil ediyor. Skala bu şekilde belirlenmiş oluyor.

0 = 250us

1 = 500us

2 = 750us

3 = 1000us

4 = 1250us

.

.

.

.

13 = 3500us

14 = 3750us

15 = 4000us

Olarak tayin ediliyor. Yukarıdaki fonksiyona göre (delay=15 yapıldığı için), nRF24L01 modül her 4000us de bir (4ms de bir), verileri tekrar yollamaya çalışacak.

count = nRF24L01 modülün verileri kaç kez göndermeye çalışacağını belirtir. Belirtilen sayı kadar deneme yapar ve daha sonra veri göndermekten vazgeçer. 0-15 arasında max. 16 değer verilebilir.

Sonraki adımda aşağıdaki tanımladığımız fonksiyon;

radio.openWritingPipe(rxAddr);

program tarafından alıcı modüle veri gönderilecek olan kanalın adres bilgisini set eder. Gönderilecek mesajın olduğu byte array aracılığı ile gönderim yapabilmek için bir Veri Kanalı (Data Pipe)  açar. Kısaca, veri gönderilecek adrese doğru, kendi üzerinde bir veri kanalı açar. Bir defada sadece bir tane veri kanalı açılabilir ancak  veri gönderilecek olan adresi değiştirebilirsiniz. Bunun için önce stopListening() fonksiyonunu çalıştırmalısınız. Adresler yine, bir bayt dizisi ile atanır. Default adres uzunluğu 5 bayt tır.

Setup() içindeki son fonksiyon olan

radio.stopListening();

fonksiyonu, nRF24L01 modülünü veri alımına kapatır. Bu fonksiyon uygulandıktan sonra modül artık dinleme yapmaz ve Verici modunda çalışmaya başlar. Write() fonksiyonu çalıştırılmadan önce stopListening() fonksiyonu çalıştırılarak modülde Alıcı modu kapatılıp, Verici modu aktif hale getirilir.

loop() fonksiyonu içerisinde, verici modülü kullanarak göndermek istediğimiz mesajı string olarak oluşturmak için bir char array değişkeni aşağıdaki gibi tanımlıyoruz. Konu ile ilgili daha fazla bilgi https://www.arduino.cc/en/Reference/String ve https://www.cs.bu.edu/teaching/cpp/string/array-vs-ptr/ adreslerinden edinilebilir.

const char text[] = “Merhaba Dunya”;

Daha sonra aşağıdaki fonksiyonu kullanarak, mesajımızı radio ismi ile tanımladığımız verici modülümüze gönderiyoruz. Modülde, openWritingPipe() ile tanımladığımız tx kanalı adresi vasıtası ile de verimizi wifi modülü ile yollamış oluyoruz.

radio.write (& text, sizeof (text));

Fonksiyondaki ilk argüman olan “ &text “ ifadesi, gönderilecek olan veriyi saklayan değişkeni gösteren bir işaretçidir.  Kütüphane dosyasında, bu fonksiyonu çalıştıracak olan fonksiyon argümanları nedeniyle  text değişkenini işaretçi ile göstememiz gerekiyor. POINTER konusu incelenirse, buradaki detay daha iyi anlaşılabilir.

Yukarıdaki fonksiyondaki ikinci argüman ise, gönderilecek olan değişkendeki verinin kaç bayt olduğunu modüle bildiriyor. Burada kullandığımız sizeof() fonksiyonu, text içindeki veri uzunluğunun kaç bayt olduğunu otomatik olarak hesaplar.

Bu fonksiyon ile bir defada 32 bayta kadar veri gönderebilirsiniz. Bu, modüün gönderebileceği max. Veri uzunluğudur. Eğer gönderilen verinin, alıcı tarafından sağlıklı bir şekilde alınıp alınmadığını öğrenmek istiyorsanız (ACK), radio.write() fonksiyonu tarafından geri döndürülen bool değeri ile öğrenebilirsiniz.  Eğer geri döndürülen bool değeri true (yani 1 ) ise, veri sağlıklı bir şekilde alıcı tarafından alınmıştır. Eğer false (yani 0) dönerse, veri alınamamıştır.

Radio.write() fonksiyonu, ACK bilgisi alana kadar veya radio.setRetries() fonksiyonu ile belirtilen deneme sayısı ve süreleri bitene kadar, programı bloke eder.

Alıcı Programı

#include <SPI.h>

#include <nRF24L01.h>

#include <RF24.h> 

RF24 radio(9, 10); 

//const uint64_t rxAddr = 0xF0F0F0F0E1LL; 

const byte rxAddr[6] = "00001"; 

void setup(){  

while (!Serial);  

Serial.begin(9600);    

radio.begin();  

radio.openReadingPipe(0, rxAddr);    

radio.startListening();

} 

void loop(){  

if (radio.available())  {    

char text[32] = {0};    

radio.read(&text, sizeof(text));        

Serial.println(text);  

}

}

 

Alıcı programı da neredeyse Verici programı ile aynı farklı olan kısımları aşağıda açıklamaya çalışacağım.

radio.openReadinPipe (0, rxAddr);

radioOpenPiepe fonksiyonu ile verileri okuyacağımız (alacağımız) kanalı ve bu kanalın adresini belirtiyoruz. 6 adet kanal tanımlayabileceğimizi yazının yukarı kısımlarında anlatmıştım. Fonksiyonun ilk parametresi olarak 0 belirtilmiş, okuma için genelde 0. kanal kullanılıyor. ikinci parametre olarak ise rxadress tanımlanmış, bu değişkene de yukarıda 5 baytlık bir dizi değişkeni tanımlanmıştı.

radio.startListening ();

fonksiyonu ile modülü veri alımına açık hale getirmiş oluyoruz. Bu andan itibaren, modül belirtilen adresten gelecek olan verileri beklemeye başlıyor.

loop() fonksiyonu içinde;

radio.available ();

fonksiyonu, önce herhangi bir veri ulkaşıp ulaşmadığını kontrol eder. Veri ulaştıysa eğer, geriye bool olarak true (1) döndürür. Veri ulaşmadıysa false (0) döndürür.

radio.read (& text, sizeof (text));

fonksiyonu ile gelen veri, char text[32]; ile tanınan text değişkenine aktarılır.

 

Umarım anlatılan konular faydalı olmuştur….

Yorum bırakın