Trådlös dataöverföring med nRF24L01

Radio-chippet nRF24L01 ger enkel dataöverföring över långa avstånd mellan två Arduino-kort. Det finns många olika färdiga moduler som använder detta chip. Vissa har en lite antenn på kretskortet och andra har extern antenn som tar större plats, men ger bättre räckvidd. Med bra antenn och fri sikt har dessa moduler en räckvidd på upp till 1000 meter. Modulen justerar datahastigheten beroende på avstånd och störningar och vid optimala förhållanden kan komma upp i 2 Mbps. Den här guiden fungerar med i princip alla nRF24L01-moduler, men är testad med modulerna från Invize.se.

För att följa den här guiden behöver du två nRF24L01-moduler och två Arduino-kort – en upsättning för sändaren och en för mottagaren. nRF24L01 kan båda sända och ta emot data och kan kommunicera med flera moduler samtidigt, men i vår guide visar vi bara kommunikation åt ett håll för att hålla det enkelt.

Kodbiblioteket

nRF24L01 kommunicerar med Arduino via SPI och har många funktioner och finesser vilket kan göra dem komplicerade att programmera. Som tur är finns det många kodbibliotek tillgänliga som förenklar användandet väldigt mycket. Biblioteket vi använder oss i den här guiden heter kort och gott ”RF24” och kan laddas ner och installeras genom bibliotekshanteraren i Arduino IDE.

Kopplingen

Förutom SPI-anslutningarna (D11, D12 och D13) så behövs ytterligare två anslutningar. Dessa kan väljas fritt och i vårt exempel här använder vi D7 för CE och D8 för CSN. Både sändare och mottagare använder samma koppling.

Beskrivning av sändarkoden

Importera bibliotek

nRF24L01 kommunicerar med Arduino via SPI och har många funktioner och finesser vilket kan göra dem komplicerade att programmera. Som tur är finns det många kodbibliotek tillgänliga som förenklar användandet väldigt mycket. Biblioteket vi använder oss i den här guiden heter kort och gott ”RF24” och kan laddas ner och installeras genom bibliotekshanteraren i Arduino IDE.

#include <RF24.h>

Definiera konstanter för anslutningar

nRF24L01 behöver, förutom SPI-pinnarna, ytterligare två anslutningar som kallas CE och CSN och vi använder defines för att specifiera vilka vi violl använda – i vårt fall D7 och D8.

#define RF24_CE_PIN 7
#define RF24_CSN_PIN 8

Skapa objekt för radio-modulen

För att använda RF24-biblioteket behöver vi skapa ett objekt som representerar radio-modulen. När vi gör det anger vi vilka anslutningar vi vill använda för CE och CSN. Eftersom vi definierat dessa ovan i koden använder vi dessa namn istället för att ange anslutningarna som nummer.

RF24 radio(RF24_CE_PIN, RF24_CSN_PIN);

Välj adress

För att många nRF24L01-moduler ska kunna finnas på samma plats utan att störa varandra så behöver man ange en unik adress för all kommunikation. Bara moduler som lyssnar på samma adress kommer ta emot datan som skickas. Adressen är ett 40-bitars tal och kan vara i princip vad som helst, men både sändare och mottagare måste använda samma.

uint64_t address = 0xF0F0F0F0E1LL;

Data-struktur

Du kan skicka vilken data som helst, men sändare och mottagare måste så klart vara överrens om hur datan är ordnad. Vi skapar en enkel struktur med en kort text-sträng och två siffror och skapar en variabel ”packet” av denna struktur.

struct {
  char msg[16];
  int x;
  int y;
} packet;

Setup

I setup() startar vi först seria-kommunkationen mellan Arduino-kortet och datorn. Denna används bara för att skicka fel- och statusmeddelanden. Vi kollar sen att vi kan prata med radio-modulen och om detta lyckas ställer vi in den till att sända med låg effekt. Detta begränsar räckvidden, men sparar ström. Vi talar sedan om för radio-modulen hur stora våra data-paket är och vilken adress vi vill sända på. Sist säger vi till den att sluta lyssna efter sändingar så att vi kan skicka istället.

void setup() {
  Serial.begin(115200);
  // Setup radio module
  if (! radio.begin()) {
    Serial.println("No radio detected");
    while (1);
  }
  radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.
  radio.setPayloadSize(sizeof(packet));
  radio.openWritingPipe(address);
  radio.stopListening();
  Serial.println("Starting");
}

Huvudloopen

I loop() använder vi oss millis() för skicka ut meddelande varannan sekund. Vi stoppar in tre olika värden i vår datastruktur – texten ”Hellow World!”, samt två slumpmässiga tal. Slutligen skickar vi datan med ett enda enkelt anrop till write(). write() vill ha en pekare data samt storleken på datan i antal byte.

unsigned long int send_timer;

void loop() {
  unsigned long int now = millis();
  if (now - send_timer > 2000) {
    Serial.println("Transmitting data packet");
    send_timer = now;
    strcpy(packet.msg, "Hellow World!");
    packet.x = random(1000);
    packet.y = random(1000);
    radio.write(&packet, sizeof(packet));
  }
}

Hela koden för sändaren

#include <RF24.h>

#define RF24_CE_PIN 7
#define RF24_CSN_PIN 8

RF24 radio(RF24_CE_PIN, RF24_CSN_PIN);

uint64_t address = 0xF0F0F0F0E1LL;

struct {
  char msg[16];
  int x;
  int y;
} packet;

void setup() {
  Serial.begin(115200);
  // Setup radio module
  if (! radio.begin()) {
    Serial.println("No radio detected");
    while (1);
  }
  radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.
  radio.setPayloadSize(sizeof(packet));
  radio.openWritingPipe(address);
  radio.stopListening();
  Serial.println("Starting");
}


unsigned long int send_timer;

void loop() {
  unsigned long int now = millis();
  if (now - send_timer > 2000) {
    Serial.println("Transmitting data packet");
    send_timer = now;
    strcpy(packet.msg, "Hellow World!");
    packet.x = random(1000);
    packet.y = random(1000);
    radio.write(&packet, sizeof(packet));
  }
}

Beskrivning av mottagarkoden

Mottagarens kod är väldigt lik den för sändaren. Hela första delen för att välja anslutningar och adress och definiera strukturen för datan som ska tas emot är identisk.

#include <RF24.h>

#define RF24_CE_PIN 7
#define RF24_CSN_PIN 8

RF24 radio(RF24_CE_PIN, RF24_CSN_PIN);

uint64_t address = 0xF0F0F0F0E1LL;

struct {
  char msg[16];
  int x;
  int y;
} packet;

Setup

Setup() börjar likadant med att kontrollera att radio-modulen går att prata med och att ställa in storlek på datapaketen. Adressen sätts däremot lite annorlunda eftersom nRF24L01 kan lyssna på flera sändare samtidigt. Vi väljer därför att allt som tas emot på vår adress ska gå genom kanal (pipe) 1. Upp till 6 kanaler (pipes) kan användas samtidigt. Verkligheten är lite mer komplicerad än så här och det finns begränsnigar på vilka adresser som går att använda samtidigt, hur kanaler anges etc. För mer detaljerad information kan du läsa manualen för nRF24L01-chippet. Det sista vi gör i setup() är att vi säger åt radion att vi vill lyssna efter datasändingar.

void setup() {
  Serial.begin(115200);
  if (! radio.begin()) {
    Serial.println("No radio detected");
    while (1);
  }
  radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.
  radio.setPayloadSize(sizeof(packet));
  radio.openReadingPipe(1, address);
  radio.startListening();
  Serial.println("Starting");
}

Huvudloopen

I vår huvudloop gör vi inget annat än att lyssna efter data som skickas från sändaren. När vi tagit emot data på adressen som vi lyssnar på så kollar vi först hur mycket data som vi fått och sedan läser vi ut den. Radio-modulen vet ju inte vad datan är för något – den tar bara emot den som en lång serie bytes och det är upp till oss att avgöra hur vi vill tolka den. Detta gör vi genom att läsa ut datan till vår paket-variabel som ser exakt likadan ut som den i sändaren. Om vi ändrar data-strukturen i sändaren måste vi även ändra den i mottagaren. När vi väl tagit emot data så skriver vi ut den till serieporten så att du kan se den i serie-monitorn på datorn.

void loop() {
  uint8_t pipe;
  if (radio.available(&pipe)) {
    uint8_t size = radio.getPayloadSize();
    radio.read(&packet, size);
    Serial.print("Data received: msg='");
    Serial.print(packet.msg);
    Serial.print("', x=");
    Serial.print((int)packet.x);
    Serial.print(", y=");
    Serial.print((int)packet.y);
    Serial.println();
  }
}

Hela koden för mottagaren

#include <RF24.h>

#define RF24_CE_PIN 7
#define RF24_CSN_PIN 8

RF24 radio(RF24_CE_PIN, RF24_CSN_PIN);

uint64_t address = 0xF0F0F0F0E1LL;

struct {
  char msg[16];
  int x;
  int y;
} packet;

void setup() {
  Serial.begin(115200);
  if (! radio.begin()) {
    Serial.println("No radio detected");
    while (1);
  }
  radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.
  radio.setPayloadSize(sizeof(packet));
  radio.openReadingPipe(1, address);
  radio.startListening();
  Serial.println("Starting");
}


void loop() {
  uint8_t pipe;
  if (radio.available(&pipe)) {
    uint8_t size = radio.getPayloadSize();
    radio.read(&packet, size);
    Serial.print("Data received: msg='");
    Serial.print(packet.msg);
    Serial.print("', x=");
    Serial.print((int)packet.x);
    Serial.print(", y=");
    Serial.print((int)packet.y);
    Serial.println();
  }
}

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *