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)
Arduino | NRF24L01 | NRF24L01 | Arduino |
---|---|---|---|
GND | GND | VCC | +3,3V (de préférence d'une alimentation extérieur) |
D9 (voir le code) | CE | CSN | D10 (voir le code) |
D13 | SCK | MOSI | D11 |
D13 | MISO | IRQ | Inutilisé 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); }