Changes between Initial Version and Version 1 of SujetTP5-2017


Ignore:
Timestamp:
Mar 2, 2017, 7:23:19 PM (8 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • SujetTP5-2017

    v1 v1  
     1= Communication sans fil =
     2
     3== Préambule ==
     4
     5Vous devez utiliser la dernière version d'arduino qui se trouve
     6{{{#!bash
     7> /opt/arduino-1.6.8/arduino
     8}}}
     9
     10== Documents de référence ==
     11
     12 * [http://www.nordicsemi.com/eng/Products/2.4GHz-RF/nRF24L01P Site Nordic nRF24L01Plus]
     13 * [https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf Spéicification nRF24L01plus]
     14 * [https://github.com/TMRh20/RF24 Repository API TMRh20/RF24]
     15 * [http://www.mon-club-elec.fr/pmwiki_reference_arduino/pmwiki.php?n=Main.ReferenceMaxi Langage Arduino]
     16
     17== Travail demandé ==
     18
     19Le but de la séance est de lire la valeur de la lumière sur une échelle de 1 à 100 sur un Arduino et de l'afficher sur le terminal d'un PC distant. Il y a donc au moins 2 noeuds, un émetteur et un récepteur.
     20Nous allons aussi voir comment simuler l'exécution de plusieurs tâches périodiques.
     21
     22== Exécution ''multi-tâches'' ==
     23
     24Avant de commencer à utiliser les modules nRF24L01+ nous allons voir comment il est possible de programmer des applications multi-tâches coopératives dans l'environnement Arduino sans pour autant alors les services d'un OS. Le code a été volontairement simplifié à l'extrème afin de bien comprendre le principe.
     25
     26Commençeons par exprimer les besoins. Dans une applications pour micro-contrôleur, il est nécessaire :
     27- d'exécuter des tâches périodiquement ;
     28- d'exécuter des tâches lorsque des événements surviennent ;
     29- de mesurer le temps séparant deux événements ;
     30- de synchroniser l'exécution de deux tâches (une tâche T1 s'exécute et produit des données qui sont récupérées par une tache T2) ;
     31- etc.
     32
     33Chaque tâche est représentée par une fonction qui code son comportement.
     34Dans l'environnement Arduino, la fonction loop() s'exécute en boucle, c'est elle qui va séquencer l'exécution des tâches.
     35La fonction loop() demande donc l'exécution des tâches à tour de rôle.
     36Les tâches n'ont pas le droit de conserver le processeur sinon celà crée un blocage du système.
     37La structure générale d'une tâche est la suivante :
     38
     39{{{#!c
     40void tache(arguments) {
     41   // test de la condition d'exécution
     42   if (evement_attendu_absent) return;
     43   // code de la tache
     44   ....
     45}
     46}}}
     47
     48Pour les tâches périodiques, nous pouvons écrire une fonction qui exploite un timer interne du processeur qui s'incrémente chaque microseconde. Cette fonction nommée `waitFor(int timer, unsigned long period)` prend deux paramètres `timer` et `period`. Le premier un numéro de timer (il en faudra autant que de tâches périoodiques). Le second est une période en microsecondes.
     49
     50`wairFor()` peut être appelée aussi souvent que nécessaire, elle rend 1 une seule fois par période (second paramètre).
     51Si elle n'est pas appelée pendant longtemps alors elle rend le nombre de périodes qui se sont écoulées.
     52
     53Dans l'application suivante nous avons deux tâches périodiques `Led()` et `Mess()`.
     54La première fait clignoter une led dont le numéro est passé en paramètre à 5Hz.
     55La seconde affiche bonjour à une fois par seconde.
     56{{{#!c
     57// unsigned int waitFor(timer, period)
     58// Timer pour taches périodique
     59// arguments :
     60//  - timer  : numéro de timer entre 0 et MAX_WAIT_FOR_TIMER-1
     61//  - period : période souhaitée
     62// retour :
     63//  - nombre de période écoulée depuis le dernier appel
     64//
     65#define MAX_WAIT_FOR_TIMER 16
     66unsigned int waitFor(int timer, unsigned long period){
     67  static unsigned long waitForTimer[MAX_WAIT_FOR_TIMER];
     68  unsigned long newTime = micros() / period;              // numéro de la période modulo 2^32
     69  int delta = newTime - waitForTimer[timer];              // delta entre la période courante et celle enregistrée
     70  if ( delta < 0 ) delta += 1 + (0xFFFFFFFF / period);    // en cas de dépassement du nombre de périodes possibles sur 2^32
     71  if ( delta ) waitForTimer[timer] = newTime;             // enregistrement du nouveau numéro de période
     72  return delta;
     73}
     74
     75void Led(int timer, long period, byte led) {
     76  static byte val = 0;                                    // valeur à mettre sur la led
     77  if (!waitFor(timer,period)) return;                     // sort s'il y a moins d'une période écoulée
     78  digitalWrite(13,val);                                   // ecriture
     79  val = 1 - val;                                          // changement d'état
     80}
     81
     82void Mess(int timer, long period, const char * mess) {
     83  if (!(waitFor(timer,period))) return;                   // sort s'il y a moins d'une période écoulée
     84  Serial.println(mess);                                   // affichage du message
     85}
     86
     87void setup() {
     88  pinMode(13,OUTPUT);                                     // initialisation de la direction de la broche
     89  Serial.begin(115200);                                   // initialisation du débit de la liaison série
     90}
     91
     92void loop() {
     93  Led (0,100000,13);                                      // Led est exécutée toutes les 100ms
     94  Mess(1,1000000,"bonjour");                              // Mess est exécutée toutes les secondes
     95}
     96}}}
     97
     98Dans la pratique, les tâches des applications communiquent entre elles. Nous pouvons le faire ici en passant par des variables globales dont les adresses sont passées en paramètres des tâches. Ce dernier point est important, il faut absolument que les variables globales ne soient pas directement utilisées par les fonctions de tâches mais toujours passées explicitement en paramètre ceci afin de faciliter la mise au point ET permettre d'avoir plusieurs instances de la même tâche.
     99
     100Dans l'exemple qui suit, nous avons ajouter une tâche !GetKbd qui lit un message au clavier et le place dans un buffer nommé mess.
     101C'est la tâche Mess qui fera l'affichage.
     102Les deux tâches se synchronisent en utilisant une case mémoire nommée full dont la valeur peut prendre deux états:
     103- 0 = le buffer ne contient pas de message, le buffer appartient à !GetKbd qui le remplit à chaque nouveau caractère
     104- 1 = le buffer contient un message, le buffer appatient à Mess qui le lit et l'affiche
     105
     106La case full est mise à 1 par !GetKbd et mise à 0 par Mess.
     107
     108Il y a une autre communication entre !GetKbd et Led que je vous laisse analyser
     109
     110{{{#!c
     111// unsigned int waitFor(timer, period)
     112// Timer pour taches périodique
     113// arguments :
     114//  - timer  : numéro de timer entre 0 et MAX_WAIT_FOR_TIMER-1
     115//  - period : période souhaitée
     116// retour :
     117//  - nombre de période écoulée depuis le dernier appel
     118//
     119#define MAX_WAIT_FOR_TIMER 16
     120unsigned int waitFor(int timer, unsigned long period){
     121  static unsigned long waitForTimer[MAX_WAIT_FOR_TIMER];
     122  unsigned long newTime = micros() / period;
     123  int delta = newTime - waitForTimer[timer];
     124  if ( delta < 0 ) delta += 1 + (0xFFFFFFFF / period);   
     125  if ( delta ) waitForTimer[timer] = newTime;
     126  return delta;
     127}
     128
     129// Variables globales pour la communication inter-taches
     130char mess[32];
     131byte full;
     132byte onled = 1;
     133
     134void Led (int timer, long period, byte led, byte *on) {
     135  static int val = 0;
     136  if (!waitFor(timer,period)) return; // sort s'il y a moins d'une période écoulée
     137  if (*on == 0) {
     138    val = 0;
     139  }
     140  digitalWrite(13,val);
     141  val = 1 - val;
     142}
     143
     144void Mess (byte *full, char * mess) {
     145  if (! (*full) ) return;
     146  *full = 0;
     147  Serial.println(mess);
     148}
     149
     150void setup () {
     151  pinMode(13,OUTPUT);
     152  Serial.begin(115200);
     153}
     154
     155void GetKbd (byte *full, char * mess, byte len, byte *on) {
     156  static byte index = 0;
     157  if (!Serial.available()) return;
     158  char c = Serial.read();
     159  mess[index++] = c;
     160  if (c == '\n') {
     161    mess[index-1] = 0;
     162    index = 0;
     163    *full = 1;
     164    *on = (strcmp(mess,"on")==0) ? 1
     165        : (strcmp(mess,"off")==0) ? 0
     166        : *on;
     167       
     168  } else if (index == len) {
     169    index--;
     170  }
     171}
     172
     173void loop() {
     174  Led (0,100000,13, &onled);           // Led est exécutée toutes les 100ms
     175  Mess (&full, mess);           // mess est un buffer d'echange avec getkbd et full une bascule de synchro
     176  GetKbd (&full, mess, sizeof(mess), &onled);
     177}
     178}}}
     179
     180== Lecture de la luminosité ==
     181
     182Faire un programme qui affiche l'état de la photo-résistance toutes les secondes, en utilisant une application multi-tâches
     183
     184== Communication nRF24L01+ ==
     185
     186=== Le matériel ===
     187
     188* Vous allez commencer par faire un schéma du noeud.[[BR]]
     189  Les composants nécessaires à un noeud sont :
     190  * un Arduino nano
     191  * un couple photorésistance, résistance
     192  * un capteur sonore
     193  * un module **nRF24L01**
     194
     195  [[Image(htdocs:jpg/nano.jpg, height=200px)]]
     196
     197  [[Image(htdocs:jpg/pinoutNRF24L01.jpg, height=200px)]]
     198  [[Image(htdocs:jpg/photoresistance.jpg, height=100px)]]
     199  [[Image(htdocs:jpg/resistance.jpg, height=100px)]]
     200 
     201=== L'usage des bibliothèques Arduino ===
     202
     203* Ce qu'il y a de bien dans l'écosystème Arduino, c'est que la volonté de faire simple.
     204  En l'occurrence, nous avons deux nouveaux objets, l'écran et le module RF24.
     205  Pour les utiliser, nous allons devoir faire appel à des fonctions, mais nous n'allons pas avoir
     206  à les écrire. En effet, pour presque tous les "périphériques" existants il existe un et même
     207  souvent plusieurs bibliothèques de fonctions écrites par des "amateurs" souvent très doués.
     208  En plus, les sources sont ouvertes, et il est donc possible d'adapter ces codes pour des
     209  besoins spécifiques.
     210
     211* Les bibliothèques sont trouvées, en général, en tapant sur un moteur de recherche, la requête
     212  "nom-du-module Arduino". Les projets sont souvent sur github. Pour faire court,
     213   * Vous téléchargez la bibliothèque (un .zip)
     214   * Vous ajoutez la bibliothèque dans l'environnement Arduino
     215   * Vous lancer l'IDE Arduino et dans le menu '''file/exemples''' vous avez un exemple (souvent plusieurs)
     216     de la nouvelle bibliothèque.
     217   * Vous en choisissez un, vous le chargez, vous le compilez, vous l'uploadez, vous le testez :-)
     218
     219=== Communication de base Sensor - baseSensor ===
     220
     221La documentation de la bibliothèque est [http://tmrh20.github.io/RF24/classRF24.html ici] dont voici un résumé :
     222
     223* `     RF24 (uint8_t _cepin, uint8_t _cspin)`[[BR]]
     224  Configuration du module radio et du SPI, reçoit les numéros de broche cepin (radio) cspin (SPI Slave Select)
     225
     226* `bool         begin (void)`[[BR]]
     227  Démarrage du module radio
     228
     229* `void         startListening (void)`[[BR]]
     230 
     231* `void         stopListening (void)`[[BR]]
     232
     233* `bool         available (void)`[[BR]]
     234
     235* `void         read (void *buf, uint8_t len)`[[BR]]
     236
     237* `bool         write (const void *buf, uint8_t len)`[[BR]]
     238
     239* `void         openWritingPipe (const uint8_t *address)`[[BR]]
     240
     241* `void         openReadingPipe (uint8_t number, const uint8_t *address)`[[BR]]
     242
     243
     244- **sensor**
     245{{{#!c
     246#include <SPI.h>
     247#include "RF24.h"
     248#include "printf.h"
     249
     250RF24 radio(9,10); // radio(CE,CS)
     251
     252byte addresses[][6] = {"0Node"};
     253
     254void setup() {
     255  Serial.begin(115200);
     256  printf_begin();
     257  radio.begin();
     258  radio.setPALevel(RF24_PA_LOW);
     259  radio.openWritingPipe(addresses[0]);
     260  radio.printDetails();
     261  delay(1000);
     262}
     263
     264
     265void loop() {
     266  Serial.println(F("Now sending !"));
     267
     268  unsigned long start_time = millis();                             // Take the time, and send it.  This will block until complete
     269  if (!radio.write( &start_time, sizeof(unsigned long) )){
     270     Serial.println(F("failed!"));
     271  }
     272 
     273  delay(1000);
     274}
     275}}}
     276
     277
     278- baseSensor
     279{{{#!c
     280#include <SPI.h>
     281#include "RF24.h"
     282#include "printf.h"
     283
     284RF24 radio(9,10);
     285
     286byte addresses[][6] = {"0Node","1Node","2Node","3Node","4Node","5Node"};
     287
     288void setup() {
     289  Serial.begin(115200);
     290  printf_begin();
     291  radio.begin();
     292  radio.setPALevel(RF24_PA_LOW);
     293  radio.openReadingPipe(1,addresses[0]);
     294  radio.printDetails();
     295  radio.startListening();
     296}
     297
     298void loop() {
     299  unsigned long got_time;
     300   
     301  if( radio.available()){
     302     radio.read( &got_time, sizeof(unsigned long) );             // Get the payload
     303     Serial.println(got_time);
     304   }
     305}
     306}}}