Arduino : Emetteur / Récepteur sans fils

Introduction

Ces petits Arduino sont magiques : petits, économes, fiables, on peut les arrêter et les redémarrer brutalement. Mais ils manquent cruellement de puissance de calcul et de capacité de stockage.

Pour résoudre ce problème, ils peuvent communiquer avec des ESP32 ou des Raspberry via un port série etc… mais également sans Fil ! et sans wifi !

Le composant NRF24L01

C’est là qu’interviennent ces petits récepteurs en 2,4Ghz (NRF24) qu’on peut connecter à un Arduino, un ESP32, un Raspberry (tout ce qui comporte un port SPI).
A l’air libre, j’ai constaté une porté intéressante (plus de 30 mètres, certains sont allés parait-il juste plus de 150m. En intérieur, ça peut-être beaucoup plus compliqué : nos intérieurs sont déjà saturés de tout un tas d’ondes radio (4G, Wifi, Bluetooth, perturbation par le câblage électrique …. Cependant cela permet d’aller bien plus loin que la limite physiques de câbles pour toute autre connexion qu’elle soit série, I2C, ou SPI. Et surtout, on peut faire communiquer plusieurs appareils ensembles (Ex : 7 Arduinos dialoguant avec un Raspberry Pi)

Voici un NRF24L01 utilisable avec ESP32, Arduino ou Raspberry Pi
Attention : il s’alimente en 3,3,V !
Sur ce schéma, les pins pointent derrière la carte. Le carré blanc autour de la borne supérieur gauche permet de repérer tout de suite la masse.

Certains ont amélioré la portée en mettant un condensateur de 10 micro Farad aux bornes de l’alimentation pour la lisser. Je n’en n’ai pas eu besoin.

Pour programmer le code qui permettra la communication, j’ai installé sur le logiciel Arduino la bibliothèque RF24 (Tmrh20)

Le composant utilise des adresses pour dialoguer. Il émet sur une et une adresse (logique) et peut écouter sur 6 adresses en même temps. Mais tous les composants peuvent écouter la même, il suffit que le message contienne un identifiant de l’émetteur et du destinataire pour s’y retrouver.
Evidement, pour que le message soit reçu il faudra qu’un récepteur écoute sur l’adresse utilisée pour l’émission.
On peut aussi choisir un canal, une puissance d’émission, une vitesse de transmission.
Pour la vitesse, j’ai choisi la plus faible (250KBS) c’est surement pour cela que je n’ai jamais eu de problème de stabilité.
Les options permettent d’utiliser des fonction natives d’ack (accusé réception) et de crc (contrôle de valité des messages transmis) je ne m’en suis pas servi.

Voici comment connecter le NRF24L01 à un Arduino (en adéquation avec le code , extraits ci-dessous)

ArduinoNRF24L01NRF24L01Arduino
GNDGNDVCC
+3,3V
(de préférence d'une alimentation extérieur)
D9 (voir le code)
CECSND10 (voir le code)
D13SCKMOSID11
D13MISOIRQInutilisé dans l'exemple

Les points importants du code :

  • L’entête
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include "printf.h"

  • Les déclarations de l’objet dialoguant avec le NFR24
// PIN_RADIO_CE=9 (D9) , PIN_RADIO_CSN=10 (D10) et vitesse du port SPI à 8 Mhz
RF24 radio(PIN_RADIO_CE, PIN_RADIO_CSN,8000000);
  • Les déclarations des adresses :
#define CTX "PIPE1" //adresse d'envoi (à croiser sur l'autre appareil)
#define CRX "PIPE2" //adresse de réception (à croiser sur l'autre appareil)
//Les adresses doivent êtres tranmissent en tableau de bytes
const byte ADRTX[6] = {CTX};
const byte ADRRX[6] = {CRX};

  • Les déclarations du message (ce sera une structure)
struct RadioPacket 
{
byte FromRadioId;
byte ToRadioId; 
byte msg[24];
};
  • dans setup, le paramétrage du composant :
    SPI.begin();
    Serial.println(F("Preparing NRF24..."));
    radio.begin(); 
    radio.setDataRate(RF24_250KBPS);
    radio.setChannel(90); 
    radio.setPALevel(RF24_PA_MIN); // choisir parmis RF24_PA_MIN -RF24_PA_LOW - RF24_PA_HIGH - RF24_PA_MAX 
    radio.disableCRC();
    radio.openWritingPipe(ADRTX); // ÉCRITURE 
    radio.openReadingPipe(1, ADRRX); // LECTURE
    radio.stopListening(); //Pour petre prêt à envoyer des données
    delay(500);
    
    printf_begin();
    radio.printDetails();  //Affiche le détail du paramétrage que le NRF24 a enregistré
    Serial.println(F("Prêt à émettre..."));
  • dans loop : son utilisation
//Je vérifie régulièrement que la radio est bien connectée et accessible
//avec clignotements de 2 LED ensembles (verte et rouge) si problème
while (radio.isChipConnected()==false)
{
    currentTime=millis(); 
    if (currentTime-previousTime>10000)
    {
       Serial.println(F("Radio RF24 inaccessible"));
       previousTime=millis(); 
       digitalWrite(ledPinKO,HIGH);
       digitalWrite(ledPinOK,HIGH);
       delay(200);
       digitalWrite(ledPinKO,LOW);
       digitalWrite(ledPinOK,LOW);
       return;
    }
}
//J'ai un bouton pour envoyer un message
buttonState = digitalRead(PIN_SIMU);
if ((buttonState == LOW) or ((currentTime-previousTime)<DELAYBETWEENREAD)))
{
    return;
}
Serial.println(F("Bouton de simulation pressé"));
previousTime=millis();
delay(10);
Serial.println(F("Prêt à émettre..."));
radioData.FromRadioId = RADIO_ID;
radioData.ToRadioId=DESTINATION_RADIO_ID;
radioData.msg="Bonjour";
radioData.IdTag[1]=nuidPICC[1];
String msg = " Envoi de ";
msg += radioData.FromRadioId;
msg += ", vers ";
msg += radioData.ToRadioId;
msg += ", msg=";
msg += radioData.msg;
Serial.println(F("Envoi..."));
radio.write(&radioData, sizeof(radioData));
Serial.print(sizeof(radioData));
Serial.println(F(" Octets"));
StartListeningTime=millis();
//boucle d'attente de la réponse
bool continuer=(millis()-StartListeningTime<10000);
//Réponse
RadioPacket ansData;

radio.startListening();
Serial.println(F("Prêt à recevoir..."));
while (radio.available() or continuer)
{
    if (radio.available())
    {
       radio.read(&ansData, sizeof(ansData)); // Lecture des données reçues, une par une
       auth=ansData.Auth;
       String msg = " réponse reçue de ";
       msg += ansData.FromRadioId;
       msg += " vers= ";
       msg += ansData.ToRadioId;
       msg += ", msg=";
       msg += ansData.msg;
       Serial.println(msg);
       if (ansData.FromRadioId!=DESTINATION_RADIO_ID) Serial.println(F("Ce message pas en provenance de l'ESP32"));
       if (ansData.ToRadioId!=RADIO_ID) Serial.println(F("Ce message n'est pas pour cet émetteur"));
       if (ansData.msg[0]==0)
       {
          Serial.println(F("Pas de réponse")); 
          digitalWrite(ledPinKO, HIGH);
          digitalWrite(ledPinOK, HIGH);
          delay(1000);
          digitalWrite(ledPinKO, LOW);
          digitalWrite(ledPinOK, LOW);
          return;
       } 
   if ((ansData.FromRadioId==DESTINATION_RADIO_ID) and (ansData.ToRadioId==RADIO_ID)) 
   { 
     //traitement de la réponse
   }
continuer=false;
}
else
{
   continuer=(millis()-StartListeningTime<TIMEOUT); 
   if (not continuer) 
   {
     Serial.println(F("Timeout de réponse"));
     digitalWrite(ledPinKO, HIGH);
     digitalWrite(ledPinOK, HIGH);
     delay(100);
     digitalWrite(ledPinKO, LOW);
     digitalWrite(ledPinOK, LOW);
     delay(100);
     digitalWrite(ledPinKO, HIGH);
     digitalWrite(ledPinOK, HIGH);
     delay(100);
     digitalWrite(ledPinKO, LOW);
     digitalWrite(ledPinOK, LOW);
    }
  }
}
Serial.println(F("Prêt à émettre..."));  //être de nouveau prêt à émettre
radio.stopListening();
delay(5);

}