Arduino Leonardo avec nRF24L01+

Voici un tutoriel qui va introduire une longue série concernant la communication sans fil entre différents matériels.

A propos du code source :

Vous remarquerez que j’attache la plus grande importance à commenter mon code. Un code bien commenté, c’est un code mieux compris. C’est aussi pour cette raison que je ne m’étale pas trop dans le tutoriel sur le code lui même.

A propos des systèmes de communications sans fils :

Pour avoir fait le tour des systèmes de communications sans fils, je dirais qu’ils ont tous des avantages et des inconvénients. Pour les différents projets à réaliser, je retiendrais essentiellement 2 ou 3 systèmes en fonction de l’usage.

Il ne faut pas oublier qu’il s’agit d’ondes et si vous multipliez le matériel utilisé, vous vous exposez à des ondes qui peuvent être néfastes. Aussi, pour cette raison, j’abandonnerai déjà la communication Wifi.

Nos objets connectés peuvent avoir à communiquer avec votre téléphone portable, la communication bluetooth peu alors être intéressante.

Pour les autres systèmes, j’ai retenu la communication via Xbee, qui permet de faire des réseaux en tout genre. Le système s’apparente à la communication série, donc fiable et rapide. Par contre, les modules ne sont pas données (Les modules pro sont parfait pour une communication longue distance, pour un drone par exemple).

Pour les autres applications intérieures, j’ai retenu la communication 2,4 Ghz pour deux raisons essentiellement : La communication bi directionnelle (Pas le cas des modules 433 Mhz en général),  leur coût peu élevé et leur taille.

Apparemment, ces modules sont suffisamment souples pour créer tout type de réseau, il y a d’ailleurs déjà une librairie mesh pour ces modules.

1. Pré requis

a. Librairies utilisées :

SPI.h : référence Arduinoexplications en Français

RF24.h : Github, et les autres RF24 qui sont contenu dans le même package.

b. Connaissance utile :

Le tutoriel n°2 permet d’appréhender l’utilisation du bouton poussoir et la gestion par automate (à état).

2. Objectif

L’objectif est assez simple, établir une communication uni directionnelle entre deux Arduino. L’un sera émetteur (Code client), l’autre sera récepteur (code serveur).

Je parle ici volontairement en effet de client/serveur, car cette base de communication nous servira par l’avenir à établir une réseau de communication.

Chaque Arduino pourra émettre sur deux canaux de communication potentiel. Le clic sur un bouton permettra de switcher d’un canal à l’autre.

Les Led :

  • Jaune : Le 1er canal de communication est activé.
  • Bleu : Le second canal de communication est activé.
  • Rouge : La communication ne passe pas ou rien n’a été reçu.
  • Vert : Les données sont bien envoyées ou reçu.

Nous allons introduire une nouvelle notion qui me semble très importante. En effet, lorsque l’on programme des micros contrôleurs, on se retrouve confronté à l’utilisation des divers composants. Mais bien souvent, nous ne voulons pas utiliser ces composants à la fréquence. En effet, ici, le clic sur le bouton doit être réactif. Le temps d’affichage des leds doit être paramétrable. La fréquence de communication d’informations doit se faire également à un rythme particulier.

Bien trop souvent dans les tutoriaux, on remarque des « delay » à tout va dans le code de la boucle loop. Si pour le temps d’affichage d’une led, je mettais directement dans le code un delay de 200 ms, c’est un temps qui serait perdu pour faire autre chose !

Vos ordinateurs ne sont que depuis très récemment multicœurs, cela n’a jamais empêché par ailleurs votre système d’exploitation de permettre à différents programmes, services, … de s’exécuter en même temps.

Il est donc possible a travers une gestion adapté de laisser la main à notre système pour faire éventuellement d’autres tâches. Sur les Arduinos, il s’agit de la boucle loop. Elle est exécuté inlassablement, il faut donc la rendre la plus souple possible.

Ainsi, nous allons utiliser la méthode millis(), qui permet d’obtenir le temps depuis lequel l’arduino est lancé. Pour chacune des gestions indépendantes, nous avons une variable diffTime ainsi qu’une constante en #define pour déterminer la fréquence.

Dans le loop, on fait des mesures pour savoir si on dépasse le temps depuis lequel on veut intervenir. Si tel est cas, on rentre dans un if d’actions (Ou il ne faut pas oublier de reconsolider la valeur temporelle). C’est simple et cela permet de gérer à merveille la temporalité des différentes actions.

Remarque : La fréquence de réception a été définie deux fois plus rapide que la fréquence d’émission. C’est pour cette raison, vous le verrez dans la vidéo que les led de réception sont tanto verte, tanto rouge. Un coup, j’ai des données, un coup j’en ai pas.

3. Matériel utilisé

  • 2 Arduino Leonardo Rev3
  • 2 module de communication nrf24l01+ : Datasheet
  • 8 Led : 2 bleu, 2 rouge, 2 vertes, 2 jaune.
  • 8 résistances de 220 Ohms (pour les Led).
  • 2 résistances de 10K Ohms  (Pour les boutons poussoir).
  • 2 bouton poussoir
  • Quelques cables, j’ai utilisé des cables à 4 broches pour les nrf24l01.

4. Schéma

tuto3_schema1

Attention le branchement des nrf24l01+ sur Arduino Uno ne se fait pas de la même façon.

5. Programme

a. Le code client

Partie déclarative, similaire dans le code serveur
// Par Sébastien DELAPORTE en 2015.
#include <SPI.h> 				// librairie utilisant le protocole SPI
#include <nRF24L01.h> 			// librairie de base nRF24L01
#include <RF24.h> 				// librairie RF24
#include <RF24_config.h> 		// librairie RF24 configuration  

/* DEFINITION DES CONSTANTES */
#define CE_ADDRESS 7          // Port CE de la communication SPI
#define CSN_ADDRESS 8         // Port CSN de la communication SPI
#define LED_TRANSMIT_OK 3     // LED de Transmission OK sur Digit 3
#define LED_TRANSMIT_KO 4     // LED de Transmission KO sur Digit 4
#define PIPE_2 5              // LED du Canal de Communication 0 sur post it sur Digit 5
#define PIPE_3 6              // LED du Canal de Communication 1 sur post it sur Digit 6
#define BTN 9                 // Bouton poussoir sur Digit 9
#define DELAY_TO_TRANSMIT 500 // Délai pour la transmission des données
#define DELAY_CLICK 200       // Délai pour la senssibilité du clic bouton
#define DELAY_LIGHT 100       // Durée d'allumage des LED de transmission.
#define DELAY_LOOP 50         // Durée d'attente dans la boucle.
#define DEBUG 1               // Mode debug : 0 => Inactif, 1 -> Actif.
/* DECLARATION DES VARIABLES GLOBALES */
// Déclaration des différents composants
RF24 radio(CE_ADDRESS, CSN_ADDRESS); 	// Utilisation du constructeur radio pin RF24 radio(CE,CSN)
const uint64_t m_comPipeList[] = { 0x48495049444102, 0x48495049444103 };	// Liste des canaux de communication
uint64_t m_CurrentPipe; 				// Canal Courant
struct
{
	byte value;
	int count;
} m_dataToTransmit; 		// Structure des données à transférer.

// Gestion de la temporalité.
long m_diffTimeBtn; 		// Mesure du temps pour le bouton
long m_diffTimeTransmit; 	// Mesure du temps pour la transmission
long m_diffTimeLight; 		// Mesure du temps d'allumage des Led de transmission.
byte btn_status; 			// Status du bouton. 0 rien, 1 press, 2 après relachement => clic

Quelques méthodes (les autres dans le .ino à télécharger)

Vous verez que j’ai également fait une méthode de Debug et on peut l’activer via une constante. Cela permet d’afficher des messages complémentaires sur le moniteur série lors de la programmation.

Les modules peuvent se paramétrer en détail, j’ai donc fait une méthode pour simplifier tout cela:

InitRF24
/* Nom : InitRf24
Description : Permet d'initialiser le module de communication sans fil RF24RL01 2,4 Ghz
Paramètre : p_level => De type CommunicationSecurityLevel permet de définir le niveau de sécurité de communication.
Nothing = 0, Low = 1, Medium = 2, High = 3, fine = 4
Nothing > Non sécurisé, pas de CRC ni d'accusé, vitesse moyenne
Low > Accusé de réception, pas de validation CRC, vitesse rapide
Medium > Accusé de réception, pas de validation CRC, vitesse moyenne
High > Accusé de réception, validation CRC 8bits, vitesse rapide
fine > Accusé de réception, validation CRC 16bits, vitesse moyenne
Retour : booléen, true si la configuré est ok, false sinon.
*/
bool InitRf24(byte p_level)
{
	bool v_isRF24RL01p = radio.isPVariant();				// True si on a un module N RF24RL01+
	bool v_return = false;
	radio.begin();											// Initialisation de la communication RF24.
	radio.setPALevel(RF24_PA_MIN);							// Puissance de la communication, au minimum tant que faire se peu.
	switch (p_level)
	{
	case 0:
		v_return = radio.setDataRate(RF24_1MBPS);			// définition de la vitesse à 1MBPS	
		radio.setAutoAck(false);							// Pas d'accusé de réception																
		radio.disableCRC();									// Pas de validation CRC.
		radio.setRetries(5, 5);								// Définie la durée d'attente et le nombre de tentative.
		break;
	case 1:
		v_return = radio.setDataRate(RF24_2MBPS);			// définition de la vitesse à 2MBPS	
		radio.setAutoAck(true);								// Accusé de réception			
		radio.disableCRC();									// Pas de validation CRC.
		radio.setRetries(5, 10);							// Définie la durée d'attente et le nombre de tentative.	
		break;
	case 2:
		if (v_isRF24RL01p)
			v_return = radio.setDataRate(RF24_250KBPS);		// Seulement si RF24RL01+
		else
			v_return = radio.setDataRate(RF24_1MBPS);		// définition de la vitesse à 1MBPS	
		radio.setAutoAck(true);								// Accusé de réception			
		radio.disableCRC();									// Pas de validation CRC.
		radio.setRetries(10, 5);							// Définie la durée d'attente et le nombre de tentative.	
		break;
	case 3:
		v_return = radio.setDataRate(RF24_2MBPS);			// définition de la vitesse à 2MBPS	
		radio.setAutoAck(true);								// Accusé de réception
															// Permet de définir la longueur de la clé CRC (Cyclic Redundancy Check) pour détecter les erreurs de transmissions.
		radio.setCRCLength(RF24_CRC_8);						// Validation CRC 8bit.
		radio.setRetries(10, 10);							// Définie la durée d'attente et le nombre de tentative.	
		break;
	case 4:
		v_return = radio.setDataRate(RF24_1MBPS);			// définition de la vitesse à 1MBPS	
		radio.setAutoAck(true);								// Accusé de réception
		radio.setCRCLength(RF24_CRC_16);					// Validation CRC 16bit.
		radio.setRetries(15, 15);							// Définie la durée d'attente et le nombre de tentative.	
		break;
	}
	return v_return;
}
Le setup
void setup(void)
{
    m_dataToTransmit.value = 0; // Init des données à transmettre
    m_dataToTransmit.count = 0;
    btn_status = 0; // Init du statut du bouton<
    InitIO(); // On initialise l'utilisation des ports de l'Arduino.
    Serial.begin(9600); // On établie la communication avec le moniteur série.
    while (!Serial); // Attente Leonardo, à ne pas oublier !
    m_CurrentPipe = m_comPipeList[0]; // On définie le canal de communication
    if (InitRf24(2)) // Permet de configurer de façon fine la communication Rf24.
    {
        radio.openWritingPipe(m_CurrentPipe); // On ouvre le canal d'écriture
        digitalWrite(PIPE_2, HIGH); // On affiche la Led.
    }
    m_diffTimeBtn = millis(); // Initialisation des variables de gestion du temps.
    m_diffTimeTransmit = m_diffTimeBtn; // Elles sont toutes initialisé à la même valeur.
    m_diffTimeLight = m_diffTimeBtn;
    Serial.println("Test Communication donnees.\n\n"); // Un petit message.
}
Le Loop
void loop(void)
{
    if ((millis() - m_diffTimeBtn) > DELAY_CLICK) // Le délai est dépassé, on peut donc checker le clic du bouton
    {
        m_diffTimeBtn = millis();
        CheckBtn();
    }
    if ((millis() - m_diffTimeLight) > DELAY_LIGHT) // Le délai est dépassé pour la durée d'affichage des leds.
    {
        digitalWrite(LED_TRANSMIT_OK, LOW);
        digitalWrite(LED_TRANSMIT_KO, LOW);
        m_diffTimeLight = millis();
    }
    if ((millis() - m_diffTimeTransmit) > DELAY_TO_TRANSMIT) // Le délai est dépassé, on peut envoyer des données et modifier les variables
    {
        m_diffTimeTransmit = millis();
        radio.powerUp(); // On réactive le RF24, sortie de l'économie d'énergie et puissance d'émission
        m_dataToTransmit.value = m_dataToTransmit.value + 1; // On modifie les données à transmettre.
        if (m_dataToTransmit.value == 255) // value évolue de 0 à 255, puis lorsqu'il repasse à zéro, count est incrémenté
            m_dataToTransmit.count = m_dataToTransmit.count + 1; // A noter que les données à transmettre on été volontairement positionné dans une structure.
        CheckPipeToTransmit(); // Vérification du canal de transmission
        Serial.println(""); // Affichage des valeurs de transmission.
        Serial.print("Canal = ");
        if (m_CurrentPipe == m_comPipeList[0])
            Serial.println("0");
        else
            Serial.println("1");
        Serial.print("Valeur = ");
        Serial.print(m_dataToTransmit.value);
        Serial.print(", Compteur = ");
        Serial.println(m_dataToTransmit.count);
        Serial.print(" --> Debut envoi : ");
        bool v_isSuccess = radio.write(&m_dataToTransmit, sizeof(m_dataToTransmit));         // On envoi les données.
        if (v_isSuccess)
        {
            StateTransmitLed(true, true); // Si l'envoi des données est ok, on allume la bonne led.
            Debug(" --> Transmission OK");
        }
        else
        {
            StateTransmitLed(true, false); // Si l'envoir des données est ko, on allume la bonne led.
            Debug(" --> Transmission KO");
        }
        radio.powerDown(); // On passe en mode repos pour le RF24, économie d'énergie
    }
    delay(DELAY_LOOP); // Attente standard du loop.
}

b. Le code serveur

méthode de réception des données
/* Nom : checkRadioCommunication
Description : Permet de vérifier s'il y a des données à réceptionner. Les réceptionnent, affiche le résultat et affiche la led en conséquence.
Retour : Aucun
*/
void checkRadioCommunication()
{
    if (radio.available())
    {
        while (radio.available())
        {
            radio.read(&m_dataToTransmit, sizeof(m_dataToTransmit));
            Serial.print("Canal = ");
            if (m_CurrentPipe == m_comPipeList[0])
                Serial.println("0");
            else
                Serial.println("1");
            Serial.print("Donnees lus : valeur= ");
            Serial.print(m_dataToTransmit.value);
            Serial.print(", compteur= ");
            Serial.println(m_dataToTransmit.count);
        }
        StateTransmitLed(true);
    }
    else
    {
        Serial.print("Canal = ");
        if (m_CurrentPipe == m_comPipeList[0])
            Serial.print("0, ");
        else
            Serial.print("1, ");
        Serial.println("rien a lire");
        StateTransmitLed(false);
    }
}

La méthode de configuration du canal. Vous noterez qu’ici, on redéfini systématiquement le canal 1. Cela permettait de vérifier qu’il sera possible de gérer plus de 6 clients (comme le permet la librairie RF24 de base).

/* Nom : SetPipeRf24
Description : Permet de changer la configuration du canal de communication.
p_pipeOffset => id du canal de communication. Les canaux sont définis dans m_comPipeList.
Ici volontairement nous utilisons le pipe 1 de la librairie RF24. Il est possible d'en configurer jusqu'à 6.
Ainsi les lectures pourraient lire 6 origines différentes. Je souhaitais voir s'il était possible de dépasser
cette limite en reconfigurant dynamiquement. C'est le cas donc ;).
Retour : Aucun
*/
void SetPipeRf24(byte p_pipeOffset)
{
    byte v_countComPipe = sizeof(m_comPipeList) / sizeof(m_comPipeList[0]);
    if (p_pipeOffset < v_countComPipe)
    {
        Debug(String("Ouverture du canal n=") + String(p_pipeOffset));
        radio.stopListening();
        radio.closeReadingPipe(1);
        radio.openReadingPipe(1, m_comPipeList[p_pipeOffset]); // Ouverture d'un canal de lecture.
        radio.startListening(); // Commençons à lire toutes les données transmises par le client.
    }
    ChangePipe(m_comPipeList[p_pipeOffset]);
}
Le setup
void setup(void)
{
    btn_status = 0; // On définie le statut du bouton à 0.
    InitIO(); // On initialise l'utilisation des ports de l'Arduino.
    Serial.begin(9600); // On établie la communication avec le moniteur série.
    while (!Serial); // Attente Leonardo, à ne pas oublier !
    delay(500);
    if (InitRf24(2)) // Permet de configurer de façon fine la communication Rf24.
        SetPipeRf24(0); // Définition du canal de communication
    m_diffTimeBtn = millis(); // Initialisation des variables de gestion du temps.
    m_diffTimeReceive = m_diffTimeBtn; // Elles sont toutes initialisé à la même valeur.
    m_diffTimeLight = m_diffTimeBtn;
}

le loop propre puisque beaucoup de méthodes. Vous noterez ici la gestion de la temporalité. Il y a trois if pour les gestions du clic souris, de l’affichage des led et de la réception des données.

Le Loop
void loop()
{
    if ((millis() - m_diffTimeBtn) > DELAY_CLICK) // Le délai est dépassé, on peut donc checker le clic du bouton
    {
        m_diffTimeBtn = millis(); // Le fait de le mettre au début de boucle permet d'être très régulier (indépendant des traitements dans la boucle)
        CheckBtn(); // Vérification état bouton et modification du statut automate.
    }
    if ((millis() - m_diffTimeLight) > DELAY_LIGHT) // Le délai est dépassé pour la durée d'affichage des leds.
    {
        digitalWrite(LED_TRANSMIT_OK, LOW);
        digitalWrite(LED_TRANSMIT_KO, LOW); // On éteint les led
        m_diffTimeLight = millis();
    }
    if ((millis() - m_diffTimeReceive) > DELAY_TO_RECEIVE) // Le délai est dépassé, on checker si on reçoit des données.
    {
        CheckPipeToListen(); // On vérifie s'il ne faut pas changer de canal.
        checkRadioCommunication(); // On regarde si des données sont arrivées
        m_diffTimeReceive = millis(); // Ici le diff Time est à la fin. Selon les durées de traitements l'écart entre
    } // deux cycles peut être supérieur à DELAY_TO_RECEIVE.
    delay(DELAY_LOOP); // Attente standard du loop. 
}

Vous retrouverez le reste du code source ci dessous.

c. Source à télécharger

Tutoriel n°3 code client fichier ino

Tutoriel n°3 code serveur fichier ino

6. Résultat

En vidéo

4 thoughts on “Arduino Leonardo avec nRF24L01+

  1. super intéressant votre site j’en ais appris.

    Mais je voudrais savoir si vous vouliez bien m’aider a faire mon projet.

    Déjas je vous dit le topo, je suis éleveur de volailles et je voudrais savoir la température dans mon poulailler.

    J’ais chez moi un arduino uno avec le nRF24L01+  la version avec l’antenne noir pliante.

    Et au poulailler un arduino nano avec le même nRF24L01+ avec pour la température un dht11 .

    En sachant que dans mes montage j’en suis a un dht11 avec écran lcd I2C avec bouton et j’arrive le faire allumer l’écran lcd et il me dit la température et le pourcentage d’humidité pendent 10 sec et il s’éteint (pour un souci d’économie d’énergie ).

    merci d’avance.

    Alex07

    1. Bonjour,

      je vois votre projet. Il n’est pas évident de vous aider ainsi à distance, mais vous devriez trouver les différents éléments clés sur mon site et notamment sur ce tuto : Gestion de la temporalité (pour l’allumage de l’écran) et gestion des nrf.
      Je vous conseil de vous rapprocher d’un Fablab prêt de chez vous, ils se feront un plaisir de vous aider à concrétiser votre projet.

  2. bonjour ou bonsoir je voudrais juste savoir comment brancher votre schéma avec un arduino uno et un nano.bonne journée ou soirée

    merci d’avance

    alex07

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.