Arduino e RF24

Arduino e nRF24L01+

In fase di sviluppo In preparazione In fase di sviluppo

In questa pagina è descritto come collegare nRF24L01+ ad Arduino e verificare il suo funzionamento. Nel limite di quanto è possibile fare con semplici strumenti di laboratorio, verranno anche mostrati alcuni dei segnali scambiati sul bus SPI e in radiofrequenza tra due apparati radio.

Preliminarmente è necessario installare le librerie RF24, direttamente dall'IDE di Arduino. La versione utilizzata è la 1.4.2, la più recente al momento della prima stesura di questa pagine:

RF24 1.4.2

La documentazione è disponibile alla pagina nrf24.github.io/RF24.

Collegamento

nRF24L01+ richiede sei fili (nota 1) per il collegamento al processore, oltre a massa ed alimentazione. Lo schema elettrico è il seguente:

Collegamento Arduino - nRF24L01p

Il collegamento fisico tra Arduino e nRF24L01+ può essere fatto in vari modi a secondo dei materiali a disposizione.

Il metodo più semplice è utilizzare un apposito circuito stampato. Purtroppo non mi risulta ce ne siano in commercio... e quindi me lo sono costruito.

In alternativa è possibile usare sette cavetti maschio-femmina DuPont, ampiamente disponibili online. La fotografia di apertura mostra una possibile realizzazione pratica e qui sotto una rappresentazione fotorealistica.

 Collegamento Arduino - nRF24L01p

È importante, oltre l'evitare errori che potrebbero facilmente essere distruttivi, garantire una buona robustezza meccanica ed una buona continuità elettrica. Alcuni consigli:

Collegamento Arduino - nRF24L01p

Test preliminari

Prima di procedere è bene verificare il corretto collegamento e la funzionalità generale del modulo. Può essere utile il seguente codice, di comprensione immediata:

#include <RF24.h>
RF24 radio(7, 8); // Imposta CE e nCSN conformemente all'hardware

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

void loop() {
  if (radio.isChipConnected())
     Serial.println ("nRF24L01p presente");
  else
     Serial.println ("nRF24L01p non rilevato");
  delay(1000);
}

I segnali SPI

Il grafico seguente mostra i segnali SPI scambiati tra Arduino a nRF24L01+ all'interno di loop():

SPI e nFR24L01p

SPI

Analisi avanzata dei segnali SPI

Analizziamo il significato dei dati scambiati tra Arduino e nRF24L01. SI tratta di 2 + 2 byte trasmessi in modalità full-duplex:

  1. Il metodo isChipConnected() legge il registro SETUP_AW (indirizzo: 0x03) di nRF24L01+ (nota 2). Per fare ciò Arduino trasmette il byte 0x03 sulla linea MOSI di SPI (verde nel diagramma temporale)
  2. Contemporaneamente nRF24L01+ trasmette il contenuto del suo registro STATUS (0x0E, una serie di flag relativi ai registri FIFO, nota 3) sulla linea MISO di SPI (rossa nel diagramma temporale)
  3. Arduino trasmette 0xFF, dato ignorato da nRF24L01+
  4. nRF24L01+ trasmette il suo registro SETUP_AW: 0x03 (0000 0011, indirizzi formati da 5 byte,  nota 3)

La frequenza del clock SPI è 8 MHz e quindi la trasmissione di un byte richiede circa 1 µs.

printPrettyDetails()

Un altro metodo utile per avere informazioni preliminari su nRF24L01+ è radio.printPrettyDetails() (nota 2) che stampa molte informazioni relative alla configurazione di nRF24L01.

printPrettyDetails()

Attività 1

Riprodurre quando sopra illustrato:

Per quanto riguarda l'ultimo punto, individuare in particolare:

  1. La frequenza RF che verrà utilizzata
  2. La velocità di trasmissione di SPI (nota 4) e del canale RF
  3. La potenza di trasmissione, anche in dBm (nota 3)
  4. La dimensione in byte del CRC, degli indirizzi e del payload
  5. Il numero di ritrasmissioni massimo in caso di errori
  6. L'indirizzo del pipe di trasmissione e dei sei pipe di ricezione

Per il significato dei termini appena utilizzati potete fare riferimento alla pagina nRF24L01+ oppure direttamente al foglio tecnico.

Trasmissione e ricezione

Per utilizzare nRF24L01+ servono due Arduino: uno configurato come Primary Transmitter (PTX) e l'altro come Primary Receiver (PRX). Il compito è certamente facilitato dalla presenza di uno dei dispositivi sicuramente funzionante e già configurato (nota 5).

Primary Transmitter

Il seguente codice trasmette un array di caratteri di lunghezza fissa (32 byte). La configurazione usata è in larga parte quella predefinita e analizzata nell'attività 1, con le uniche modifiche evidenziate nel codice (nota 6).

#include <RF24.h>
#include <printf.h>
#define bufferSize 32
const byte address[6] = {0x30, 0x30, 0x30, 0x30, 0x32}; // Indirizzo del pipe
int counter=0;
char Hello[bufferSize];

RF24 radio(7, 8); // Imposta CE e nCSN conformemente all'hardware
void setup() {
  Serial.begin(9600);
  printf_begin();
  radio.begin();
  radio.setAutoAck(false); // Disattiva la richiesta di ACK
  radio.openWritingPipe(address);  // Imposta l'indirizzo del pipe di trasmissione
  radio.stopListening();
  radio.printPrettyDetails();
}

void loop() {
  sprintf(Hello, "Ciao mondo %i", counter++); // Personalizzare il testo trasmesso
  radio.write(Hello, bufferSize);
  delay(1000);
}

Alcuni fake nRF24L01+ potrebbero avere problemi nel funzionare con il codice mostrato. Potrebbe essere utile aggiungere la seguente riga di configurazione:

radio.setPALevel(RF24_PA_MIN); // Imposta la potenza RF al minimo

Inoltre, solo se è possibile modificare anche il ricevitore:

radio.setDataRate(RF24_2MBPS); // Imposta la velocità a 2 Mbps

Infine, soprattutto se si usa Arduino Nano, potrebbe essere utile inserire tra massa e VDD (3.3 V) un condensatore, possibilmente al tantalio, da qualche decina di microfarard.

Durante il debug è utile stampare il contenuto del frame trasmesso in esadecimale con una funzione simile alla seguente:

void printBuffer(uint8_t buffer[], int len) {
  for (int i = 0; i < len; i++) {
    Serial.print(buffer[i] < 16 ? "0" : "");
    Serial.print(buffer[i], HEX);
    Serial.print(" ");
  }
  Serial.println(" ");
}

Attività 2

IMPORTANTE Questa attività può essere portata a termine solo se è disponibile un Primary Receiver già configurato; altrimenti occorre completarla in parallelo alla Attività 2bis (nota 5).

  1. Configurare Arduino per comportarsi come Primary Transmitter
  2. Verificare il pacchetto ricevuto dal Primary Receiver
  3. Stampare il contenuto del pacchetto trasmesso in esadecimale

Primary Receiver

Il seguente codice riceve un array di caratteri di lunghezza fissa (32 byte). La configurazione usata è in larga parte quella predefinita e analizzata nell'attività 1, con le uniche modifiche evidenziate nel codice (nota 6).

#include <RF24.h>
#include <printf.h>
#define bufferSize 32
const byte address[6] = {0x30, 0x30, 0x30, 0x30, 0x32}; // Indirizzo del pipe
char Received[bufferSize];

RF24 radio(7, 8);                // Imposta CE e nCSN conformemente all'hardware

void setup() {
  Serial.begin(9600);
  printf_begin();
  radio.begin();
  radio.openReadingPipe(0, address); // Imposta l'indirizzo del pipe 0 di ricezione
  radio.setAutoAck(false);           // Disattiva la richiesta di ACK
  radio.printPrettyDetails();
  radio.startListening();
  Serial.println("Pronto...");
}

void loop() {
  if (radio.available()) {
     radio.read(Received, bufferSize);
     Serial.println(Received);
  }
}

Attività 2bis

IMPORTANTE Questa attività può essere portata a termine solo se è disponibile un trasmettitore già configurato; altrimenti occorre completarla in parallelo alla Attività 2 (nota 5).

Attività 3

Dopo aver configurato un Arduino come Primary Receiver ed un altro come Primary Transmitter  (attività 2bis), modificare alcuni dei parametri di comunicazione. IMPORTANTE: procedere modificando un solo parametro alla volta, agendo in parallelo sia sul PTX che sul PRX.

Analisi dello spettro

Ricapitolando le caratteristiche del segnale radio generato da nRF24L01+:

Le due immagini seguenti mostrano cosa è visibile con un analizzatore di spettro (Aaronia HF-6065-X V3):

nRF24L01+ Spettro con modulazione a 1 Mbit/s

Comunicazione bidirezionale

Il codice di partenza per una comunicazione bidirezionale è il seguente, identico per trasmettitore e ricevitore:

#include <RF24.h>
#define bufferSize 32
const byte address[6] = {0x30, 0x30, 0x30, 0x30, 0x32}; // Indirizzo del pipe

RF24 radio(7, 8);                // Imposta CE e nCSN conformemente all'hardware

void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.setAutoAck(true);            // Attiva la richiesta di ACK
  radio.openWritingPipe(address);    // Imposta l'indirizzo del pipe di trasmissione
  radio.openReadingPipe(0, address); // Imposta l'indirizzo del pipe 0 di ricezione
  radio.startListening();
}

void loop() {
  char Hello[bufferSize];
  String string;

  if (Serial.available()) {
    string = Serial.readString();
    string.toCharArray(Hello, bufferSize);
    radio.stopListening();
    radio.write(Hello, bufferSize);
    radio.startListening();
  }
  if (radio.available()) {
    radio.read(Hello, bufferSize);
    Serial.print(Hello);
  }
}

Attività 4

Analizzare il comportamento del codice di comunicazione bidirezionale proposto e provarlo utilizzando due Arduino. Se si è in una ambiente con molti trasmettitore attivi, per esempio un laboratorio scolastico, impostare un canale RF libero (nota 8).

Attività 5 AVANZATO

Modificare il codice per usare due pipe con indirizzi diversi, uno per la ricezione ed uno per la trasmissione. Ovviamente in questo caso il codice del ricevitore e quello del  trasmettitore non saranno identici.

Attività 6  AVANZATO

Il dispositivo PIC-WL-Sen trasmette ogni 8 secondi alcune misure ambientali. Scrivere il codice che riceve il pacchetto e stampa i vari campi.

La configurazione:

Nella versione base il pacchetto contenuto nel payload è una struct di 30 byte:

#define ID_LEN 6 // Lunghezza in byte dell'identificatore univoco del dispositivo
struct frame_t {
  uint8_t vID;           // Identificativo della versione del pacchetto (0x4A)
  uint8_t flags;         // 8 bits: ciascun bit identifica la presenza o meno di una campo "misura"
                         // Esempio: 1100 0000 -> VDD + Temperatura + 6 campi non implementati
  uint16_t mID;          // Numerazione progressiva dei pacchetti
  uint8_t devID[ID_LEN]; // Identificatore univoco del dispositivo
  uint16_t vdd;          // Tensione della batteria [mV]
  int16_t temperature;   // Temperatura, in centesimi di °C (Esempio: 36,5°C -> 3650)
  uint16_t measure[6];   // Altre misure ambientali, non implementato
  uint32_t S;            // Secure hash, non implementato
};

Il contenuto può essere stampato come sequenza di 30 byte:

Buyte ricevuti in esadecimale

... oppure decodificando i vari campi che costituiscono la struct:

Pacchetto decodificato

Note

  1. La libreria RF24 non utilizza l'uscita IRQ di nRF24L01+
  2. RF24 Class Documentation
  3. Foglio tecnico di nRF24L01
  4. printPrettyDetails() sembra ritornare un'informazione errata relativamente alla frequenza del clock SPI e quella indicata non è la velocità effettiva, ma quella massima impostata dal codice in RF24::beginTransaction()
  5. In genere utilizzo in classe un PRX già configurato che proietta sul videoproiettore tutti i payload ricevuti e trasmette un payload diverso una volta al secondo
  6. Questo codice permette di utilizzare un unico PRX, anche per molti PTX
  7. In ambiente scolastico può essere usato il doppio del numero di registro: 2 → 62 per selezionare le frequenza tra 2 402 MHz e 2 462 MHz. L'uso dei soli valori pari garantiscono il funzionamento anche a 2 Mbps, quando la banda occupata dal singolo canale RF è 2 MHz
  8. Contrariamente a quanto è ragionevole pensare, a volte la comunicazione ha un comportamento migliore alla velocità più elevata, probabilmente a causa della minor durata del frame


Pagina creata nel dicembre 2021
Ultima modifica: 7 gennaio 2022


Licenza "Creative Commons" - Attribuzione-Condividi allo stesso modo 3.0 Unported


Pagina principaleAccessibilitàNote legaliPosta elettronicaXHTML 1.0 StrictCSS 3

Vai in cima