Francenanorecif

Bienvenue, Invité
Nom d'utilisateur : Mot de passe : Se souvenir de moi

SUJET : ARDUINO by The_ccm [Tuto N°8]

ARDUINO by The_ccm [Tuto N°8] 18 Nov 2011 14:21 #306924

  • unjall
  • Portrait de unjall
  • Hors Ligne
  • Modérateur
  • Messages : 6585
  • Remerciements reçus 124
Avant Propos

Pas facile pour un expert en récifal de faire une gestion électronique quand on est débutant dans le domaine. Pas facile pour un débutant en récifal (comme moi) de connaître assez bien l’Arduino mais ne pas trop savoir l’appliquer à un gros cube d’eau salée.

Beaucoup veulent se lancer dans une gestion automatisée de leur aquarium, mais ne savent pas vraiment par quel bout commencer.

Je vais essayer de présenter l’Arduino et quelques applications pratiques de celui-ci, accessible au débutant. Je n’ai pas la prétention de dire que ce post est meilleur que tous les autres traitant du sujet, certains sont très bon mais ne sont pas forcément compréhensible par un non initié, et puis tout le monde participe, les questions se croisent, se perdent, et c’est normal, c’est un forum où tout le monde s’exprime. J’espère simplement que cet article en sera la synthèse.

Certaines notions seront sans doute simplifiées, dans un souci de meilleure compréhension.

L’art de quoi?

L’Arduino est simplement une carte électronique très complète, comprenant un micro contrôleur, c’est à dire une puce pouvant contenir un programme, et exécutant une série d’instructions. On peut y brancher de nombreuses choses, à peu près ce qu’on veut. Il y a des entrées et des sorties, de tout type.

2 types de cartes :

Arduino Uno





Arduino Mega





Ces 2 cartes sont à peu près communes, on y trouve :

- 1 connecteur d’alimentation pour la carte et ce qui sera branché dessus, généralement un transformateur mural 12V suffit.

- 1 connecteur USB pour programmer la mémoire flash qui contiendra le programme et servira à "débugger" celui ci lors de l’exécution.

- Plusieurs entrées/sorties numériques :
on peut par exemple allumer une led, brancher un interrupteur/clavier, un capteur de température numérique, connecter un écran.....

- Plusieurs entrées/sorties analogiques :
pour mesurer une tension, y brancher un potentiomètre......

- Un ou plusieurs ports séries :
il est possible d’interfacer un PC

- Plusieurs entrées/sorties avec des protocoles de communications rependus tels que:
I2C, SPI, ne nous attardons pas là dessus.

La différence entre les 2 cartes est simplement le nombre d’entrées/sorties, à ma connaissance bien que je ne connaisse pas la Uno les fonctions sont similaires.

Je ne saurais que conseiller l'Arduino Mega qui permettra de mettre plus de choses, même si le projet de départ n’est pas très ambitieux, celà permettra de ne pas être bloqué par la suite.
La différence de coût est faible.


Que mettre dessus ?

- Leds:
Utile si on veut voir visuellement les états de fonctionnements de vos appareils, par exemple associer une Led à une pompe de brassage, à l’osmolateur, pour savoir si ceux ci sont en marche.

- Ecran LCD:
Pour pouvoir afficher tout un tas d’information, la température des capteurs, la date et l’heure, etc....

- Horloge:
Module permettant de sauvegarder la date est l’heure avec sa pile bouton de sauvegarde.
Même si la gestion de l’heure peut se faire dans le programme de l’arduino sans ce module, sa présence est fortement recommandée, de plus en cas de panne secteur l’heure est sauvegardée par la pile.
Se connecte sur l’Arduino sur le Bus I2C avec 4 fils.

- Carte relais:
Il n’y a pas d’entrées sorties en 220V sur l’Arduino, la carte relais permet donc de commander vos pompes, écumeurs, osmolateur, et tout le reste.
Se connecte de la même manière sur le bus I2C avec 4 fils, il est complètement possible de mettre plusieurs modules sur le bus I2C en même temps.

- Rampe de Led dimmable:
Si votre rampe d’éclairage Led dispose d’une entrée dite "Dimmer", pour régler son intensité.

- Capteur de température numérique:
Pour mesurer la température de votre bac, mais aussi de la rampe de Led par exemple.

- Récepteur Infra-rouge:
A l'aide d'une télécommande universelle on peut piloter son Arduino du bout des doigts !




Que faire avec tout ça ?

Bonne question, une étape très importante est de bien définir son projet, et donc son budget.

Ce que peut faire l'arduino :

- Gérer l'heure,
- Gérer l'allumage / l'extinction progressive d'une rampe de led en fonction d'un calendrier ou de
tranches horaires,
- Commander les pompes de brassage,
- Commander l'écumeur,
- Commander un osmolateur via une vanne on/off,
- Mesurer des températures (eau, mais aussi rampe de led),
- Commander la vitesse de vos ventilateurs (rampe de led, ventilateur de surface),
- Enregistrer vos données sur une carte SD (avec le module ethernet shield),
- Envoyer vos données sur un serveur (avec le module ethernet shield), générer une page Web consultable
à distance.
Qu''importe le flacon pour vu qu'on ait l'ivresse...
Dernière édition: 22 Déc 2011 11:04 par unjall.
Le sujet a été verrouillé.
Cet utilisateur a été remercié pour son message par: poupsea57

Re: ARDUINO by The_ccm Part.1 18 Nov 2011 15:01 #306930

  • unjall
  • Portrait de unjall
  • Hors Ligne
  • Modérateur
  • Messages : 6585
  • Remerciements reçus 124
1) Descriptif du projet


Programme n°1: "Je lis et j'affiche l'heure."

La première chose à gérer est l'heure.
Ne serait-ce que pour pouvoir piloter la ou les rampes Led.
Ce premier montage est relativement simple, facile à câbler et demande peu de code de programmation.


Avec une platine Arduino Mega :

- Gestion de l'heure
- Gestion des rampes Leds
- Gestion des pompes, écumeurs via la carte relais
- Gestion de l'osmolateur via une électrovanne 12V
- Mesure de la température de l'eau
- Mode nourriture (arrêt des pompe / écumeur pendant un lap de temps)
- Affichage sur un LCD de 4x20 caractères
- Sauvegarde des données sur carte SD

Le tout réagissant aux ordres via une télécommande universelle en infrarouge (trop dur de cabler des boutons !)



2) Préparatifs

- Difficulté : ♠♠
- Temps nécessaire : environ 1h pour le câblage, et autant pour le programme.
- Coût du projet et liste du matériel :
- Fourchette de prix suivant les fournisseurs : entre 60 et 100€
- Arduino Mega : entre 30 et 50€
- Un LCD de 1,2,4 lignes et 12,16 ou 20 caractères de large. Peu importe la taille, il faut que celui-ci soit compatible HDD44780, référence barbare de la puce sur celui-ci, ou une équivalente. Entre 10 et 25€

- Module horloge DS 1307. Entre 7 et 15€

- Transformateur 12V 500mA : 10€

- Une résistance de 1k, 2.2k, 4.7k, ou un potentiomètre
- Un cable USB pour la programmation
- Fil de câblage
- De la zénitude, des doigts pas trop gros.

Quelques notes techniques :

Sur le LCD, pour reconnaître le bon modèle celui ci possède en général 16 broches avec les noms
suivants :

- Alimentation VCC/GND : 5V et masse.
- Broche pour le réglage du contraste.
- RS : Sélection du registre d'écriture
- R/W : broche lecture/écriture. On écrit dans la mémoire du LCD , ou on lit des infos dans celle-ci.
- EN : Enable, pour activer le LCD
- 8 broches D0 à D7 : pour envoyer/réceptionner les données
- Alimentation du rétro-éclairage : 5V et masse.

La photo un peu plus haut est le type de LCD qui convient.
Peu importe le nombre de lignes et de caractères en largeur, il y aura 16 broches.

Module Horloge :

Il doit être avec une connectique dite "I2C", soit:
4 broches, alimentation VCC ,5V et masse, broche d'horloge SCL et broche de données SDA.
Encore une fois la photo un peu plus haut est représentative.

Le transfo d'alimentation aura une tension de 9V,12V et une puissance mini de 500mA pour être confortable.


Fournisseurs :

Arduino et périphériques :
(HK) www.dfrobot.com/
(HK) www.sure-electronics.com/index.php
(UK, Ebay, livraison rapide) search.ebay.fr/?sass=droboticsonline&ht=-1
(UK, bon prix) www.earthshineelectronics.com/54-arduino-products
(FR) www.easyrobotics.fr/catalog/

Composants courants :
(VPC, livraison rapide, port 4.70) : www.gotronic.fr/
(VPC et Paris nation, fouilli mais très complet) web1.ibcfrance.fr/
(VPC et 92) : www.electronique-diffusion.fr/

Composants courant et spécifiques
(VPC US,prix imbattables, livraison 3j UPS, port 9$) : www.futureelectronics.com/WebsiteLanding.aspx


3) Réalisation Matérielle


Il faut connecter le module horloge et le module LCD sur l'Arduino.

Par soucis d'économie de ressources, le LCD est cablé en mode "4 bits", c'est à dire que l'on utilise
uniquement les broches D4 à D7, l'envoie des données se fera en 2 fois, celà prendra plus de temps , mais ce ne sera pas visible.

L'alimentation du rétroéclairage peut être brancher directement sur les bornes d'alimentation du LCD.
Il est très important de cabler la broche 3 (VO) de réglage du contraste !
Si elle n'est pas cablée, l'affichage ne fonctionnera pas.

- Solution 1 :
Mettre la broche à la masse via la résistance, le contraste peut être par contre trop
fort, mais pour les tests, celà suffira. on verra par la suite la solution Top

- Solution 2 :
Mettre un potentiomètre entre les broches VCC, GND et la broche 3.

- solution Top :
Commander et régler le contraste avec l’Arduino

ATTENTION

Toujours brancher l'alimentation et le cable USB en dernier !

Le module horloge ne peut se brancher qu'à un endroit, c'est à dire sur le port I2C de l'Arduino.
Pour le module LCD, n'importe quelle broche digitale "numérique" peut convenir.

Sur l'Arduino, certaines broches, suivant leur programmation, peuvent avoir différentes fonctions.
Aussi il est préférable d'utiliser des broches digitales qui n'ont que cette fonctionnalité et ainsi ne
pas en condamner d'autres dont vous pourriez avoir besoin par la suite.

Aussi je recommande fortement le schéma final suivant :



Résumé des connexions :

Module horloge :

broche SCL sur broche 21 de l’Arduino
broche SDA sur broche 20 de l’Arduino
+VCC et GND au choix sur l’Arduino

LCD (connection côté LCD – côté Arduino)

Broche 4 – Broche 30
Broche 5 – Broche 31
Broche 6 – Broche 32
Broche 7 à 10 à la masse
Broche 11 – Broche 36
Broche 12 – Broche 37
Broche 13 – Broche 38
Broche 14 – Broche 39
Broche 15 – VCC
Broche 16 – GND

4) Le programme

Vous aurez besoin :

- du logiciel Arduino pour la programmation de ce dernier : Logiciel
- La librairie RTCLib pour le module Horloge : Librairie

Il faut décompresser le dossier dans le répertoire librairie et redémarrer le programme Arduino si celui-ci est lancé.

Les grands structures très simplifiées d'un programme pour Arduino :

- La partie "Include" :
Vous référencez des librairies externes (bout de code) qui faciliteront grandement l'écriture du programme. Il existe pleins de librairie toutes faites par la communauté des développeurs, par exemple pour piloter un capteur de température, la carte relais, l'horloge, etc.

- La partie variables globales :
C'est ici où vous déclarerez des variables qui pourront être utilisées dans toutes les fonctions de base de l'Arduino et les fonctions que vous écrirez.

- la fonction setup() :
Cette fonction, facultative mais quasi toujours présente, est exécutée une seule fois au démarrage,donc après Reset forcé ou involontaire, ou alimentation de l'Arduino.
Dans cette fonction, on initialisera l'Arduino, dire si certaines broches sont en entrées ou en sorties, leur mode de fonctionnement, l'initialisation du port Série pour le debug, etc.

- la fonction loop() :
Ce sera l'emplacement où le programme principal sera écrit.
Par définition, c'est une boucle, c'est à dire qu'après la dernière instruction de la fonction loop(), on revient à la première instruction de la même fonction, et on exécute à nouveau, sans cesse, sans fin.
Ne pas oublier cette notion très importante de boucle. L'Arduino exécute donc le programme en mode séquentiel

- Avantage du mode séquentiel :
Programme exécuté toujours dans le même ordre, sans se poser de questions.
La programmation est simplifiée est convient dans la plupart des cas.

- Désavantage :
On ne gère aucune notion de priorité dans le programme.
Exemple, je gère mon osmolateur avec un capteur de niveau d'eau et une électrovanne, celle ci est en marche et mon niveau monte, pendant ce temps j'exécute une fonction qui me prend beaucoup de temps, pire qui plante dans une boucle, et mon aquarium déborde, et mon osmolateur ne s'arrête jamais.

Il est possible en mode un peu plus avancé de gérer des priorités dans les programmes, avec les
interruptions.
Ainsi, je peux dire que quoique mon programme soit en train de faire, si mon capteur de niveau d'eau détecte un niveau haut, j'interromps mon programme pour me "brancher" sur une fonction qui arrêtera mon osmolateur. Mais ça, ce sera pour plus tard.


Organigrame simplifié



On retrouve la fonction Setup() avec l'initialisation des différentes choses.
La fonction loop() se contente d'appeler 2 fonctions que nous écrirons, GetTime() et FlushLCD()
On écrira également l'heure sur le port série pour le débug.

1. La partie "include"
#include <LiquidCrystal.h>
#include <Wire.h>
#include <RTClib.h>


Ici, on indique les librairies externes utilisées, LiquidCrystal pour la gestion du LCD, et Wire et RTClib pour la gestion de l'horloge.

2. La partie variable et alias
#define LCD_WIDTH 20 
#define LCD_HEIGHT 4
char lcdbuf[LCD_WIDTH];
uint8_t DsHour ,DsMin ,DsSec;
LiquidCrystal lcd(30, 31, 32, 36, 37, 38, 39);
RTC_DS1307 rtc; 

Les lignes " #define# " permettent d'associer dans le cas présent un Label à une constante.
Ici on définit associe la largeur et la hauteur du LCD à 2 label, que l'on utilisera plus tard.

Les label ou alias sont très pratique pour définir une broche, par exemple "#define LED_VERTE 34" permettra d'utiliser le Label LED_VERTE partout dans le programme pour commander votre Led connectée sur la broche 34 de l'Arduino.
Si, plus tard, vous devez changer la connexion de votre led sur une autre broche, vous avez juste à redéfinir l'alias, inutile de modifier tout le programme.
De plus, un " #define# " ne prend aucune place en mémoire pour le programme.
En effet, c’est le compilateur qui remplace les label dans le programme par le valeur définit au début, ce qu’on appelle une directive de compilation.
Lorsque vous avez besoin d’une valeur qui ne changera jamais au cours du programme, préférez l’utilisation de define.

"lcdbuf" servira à la mise en forme pour l'affichage

"DsHour" ,"DsMin" ,"DsSec" seront nos variables globales pour récupérer la valeur de l'horloge.
Le type "uint8_t" est un type entier, donc équivalent à "int", mais qui ne pourra pas prendre de valeur négative.
En contrepartie, le bit réservé au signe dans "int" est libre et permet une plus grande capacité, là ou "int" accepte des valeurs entre -32768 et 32767, "unsigned int" permet des valeurs comprises entre 0 et 65535.
Voici un résumé des types possible en "C++" , en théorie ils sont tous disponibles pour l’Arduino :

Résumé

lcd est un objet qui nous permettra de piloter l'afficheur LCD simplement.
Dans cette déclaration, les chiffres indiquent la connexion des broches du LCD dans l'ordre les broches RS, RW, EN, D4, D5, D6 et D7.
Ces valeurs doivent donc correspondrent à votre cablage

"rtc" est l'objet qui nous permet de piloter le module horloge.

3. La fonction "setup()"

Nous avons nos variables et librairies déclarées, nous pouvons maintenant écrire le programme.
D'abord la fonction d'initialisation setup() qui je le rappelle ne sera exécutée qu'une seule fois au démarrage.
void setup(void)  
{    
  Serial.begin(9600);
  Serial.println("Entrée dans Setup()");
  Wire.begin();
  rtc.begin();
  lcd.begin(LCD_WIDTH, LCD_HEIGHT,1);
  if (! rtc.isrunning()) 
    {     
    Serial.println("Module RTC non initialisé !");
     //following line sets the RTC to the date & time this sketch was compiled     
    rtc.adjust(DateTime(__DATE__, __TIME__));   
    }    
  lcd.clear();  
} 

Les 2 premières lignes initialisent le port série, qui est en fait notre port USB connecté à l'ordinateur. Ainsi tout ce qui sera envoyé sur ce port pourra être lu sur l'ordinateur.
La ligne "Entrée dans Setup()" est importante, car si vous la voyez s'afficher une fois que vous avez passé cette fonction, donc vous etes dans le programme principal de la fonction loop(), celà signifie que nous sommes rentrés une deuxième fois dans setup() et donc que l'Arduino a rebooté.
On la garde donc, quand le programme sera plus complexe celà peut donner des indications précieuses.
Il est facile de faire rebooter un Arduino en utilisant des types de variables non adaptées à la valeur que l'on stockera dedans par exemple.

"Wire.begin" et "rtc.begin" initialisent le module horloge et le port I2C.
"lcd.begin" initialise le LCD en passant en paramètre sa largeur et sa hauteur.
if (! rtc.isrunning()) {


à quoi sert ce test ?

Lors de la première alimentation du module horloge, celui-ci n'est pas paramétré, l'heure est donc 00:00:00 et la date 01/01/0001.
La date et l'heure ne sont pas à jour.
Ce sera le cas à chaque fois que l'alimentation sera coupée et la pile de sauvegarde trop faible ou non présente.
Dans ce cas, "rtc.isrunning" renverra la valeur false

Donc, la première fois seulement, nous rentrons dans le test if.
Nous affichons "Module RTC non initialisé !" sur le port série.

Puis cette ligne est exécutée:
rtc.adjust(DateTime(__DATE__, __TIME__));


les variables "__DATE__" et "__TIME__" sont pratiques, elle représente la date et l'heure du PC au moment de la compilation.
Donc la fonction adjust va paramétrer le module horloge avec la date et l'heure courante.
Celà veut dire qu'une fois le programme compilé, il ne faut pas tarder à l'envoyer sur l'Arduino.

Au prochain reboot : le module horloge aura sa date et son heure en mémoire, la fonction "isrunning()" renverra "true" et nous ne rentrerons pas dans le test.

enfin "lcd.clear" efface notre LCD par sécurité.

4. La fonction "loop()"
void loop(void)  
  {    
  GetTimeFromRTC();    
  Serial.print("Heure RTC : "); 
  Serial.println(lcdbuf);  
  FlushLCD();  
  delay(900);
} 

rien de méchant:

- On appelle notre fonction "GetTimeFromRTC()" qui ira lire l'heure dans le module horloge
- On affiche la date et l'heure sur le port Série
- On affiche la date et l'heure sur le LCD
- On attends 900ms
- Et pour rappel, comme on boucle, on retourne à la première ligne de la fonction "loop()", soit l'appel de "GetTimeFromRTC()"

5. La fonction "GetTimeFromRTC"
void GetTimeFromRTC() 
  {  
  DateTime now = rtc.now();     
  DsHour = now.hour();  
  DsMin = now.minute();  
  DsSec = now.second(); 
} 

On déclare une variable "now" et on appelle la fonction du même nom de l'objet "rtc", l'heure est récupérée du module dans la variable.
Ensuite, les 3 lignes associent les heures, minutes, secondes à nos 3 variables globales définies en début de programme.
Les variables sont globales donc nous pouvons lire leur contenu n'importe où ailleurs dans le programme.

6. La fonction "FlushLCD"
void FlushLCD() 
{  
  lcd.setCursor(0,0);  
  sprintf(lcdbuf, "%.2d:%.2d:%.2d",DsHour,DsMin,DsSec); 
  lcd.print(lcdbuf);
} 

On se positionne au début du LCD, première ligne, premier caractère.
"sprintf" permet de formater nos valeurs dans une chaine de caractères.
Ici on indique que "DSHour", "DsMin" et "DsSec" seront toujours affichées sur 2 caractères, pratique pour les minutes et secondes inférieures à 10, le zéro sera mis automatiquement.
Inutile de faire de test if sur la valeur comme on voit dans beaucoup de programmes.

enfin "lcd.print" envoie le contenu de la variable "lcdbuf" formatée au dessus sur le LCD.
Le programme complet : ICI


Si cela ne fonctionne pas

Pas évident quand ça ne fonctionne pas, dans l'ordre :

- Vérifier les connexions des différents éléments.
- Vérifier l'alimentation de l'Arduino.
- Se lancer dans le debug du programme, pour celà vous pouvez ouvrir la fenêtre Serial Monitor du programme Arduino sur l'ordinateur et mettre des Serial.
"println" partout où bon vous semble dans votre programme, ainsi vous pourrez suivre son déroulement facilement.

5) Conclusion

Voilà un premier programme très simple, peu utile, mais qui permet une première approche de l'Arduino.

6) Renvois vers le post commentaires dans la rubrique DIY

Post Part.1 de The_ccm
Qu''importe le flacon pour vu qu'on ait l'ivresse...
Dernière édition: 23 Déc 2011 08:10 par unjall.
Le sujet a été verrouillé.

Re: ARDUINO by The_ccm Part.2 23 Déc 2011 06:49 #311153

  • unjall
  • Portrait de unjall
  • Hors Ligne
  • Modérateur
  • Messages : 6585
  • Remerciements reçus 124
1) Descriptif du projet


Avant toute chose, je vous conseille fortement de lire la première partie du tutoriel, qui présente les bases de l’Arduino.
Vous aurez également besoin de réaliser grosso modo le même montage que la part.1
Cela ne peut être que bénéfique.
J’y ferai de plus référence pour la liste du matériel notamment.


Le besoin :

Piloter progressivement (en fonction de l’heure) l’allumage / l’extinction d’une ou plusieurs rampes Led, à condition d'avoir un driver compatible!
C’est à dire avec une entrée pour le pilotage de l’intensité lumineuse.
Si vous possédez un driver simple sans cette entrée, ce sera donc "ON/OFF" et la commande se fera en commutant le 220V sur une carte relais (ceci fera l’objet entre autres d’un troisième tuto).

2) Préparatifs


Difficulté : ♠♠
Temps nécessaire : environ 1h pour le câblage, 1h pour le programme.
Coût du projet et liste du matériel : les mêmes composants que pour le tuto Part.1, à savoir:
- Une platine Arduino Uno (duemilanove ou Mega) je recommande toujours cette dernière pour environ 5 à 10€ de surcoût.
- Un afficheur LCD.
- Un module horloge RTC.
Pour le choix des composants voir le tuto Part.1
En supplément :
- Un driver Led avec entrée dite "PWM".


Quelques notes techniques:


Choix du Driver :

Il doit posséder une entrée dite "PWM" pour le réglage de l’intensité.
Une entrée "PWM" est une entrée 0/5V, voir 0/10V dont l’intensité est fonction du rapport cyclique du signal carré appliqué à cette entrée.
Pour la définition d’un signal "PWM" voir cette page : ICI

Le choix du driver est aussi déterminé par le nombre de Led que vous souhaitez connecter.
Leur tension dite « de forward » typiquement entre 3 et 4V est le courant nécessaire pour les LED.
Le driver retenu pour ce tuto est le: MeanWell ELN60-48P.


Pas le plus pratique à connecter mais c’est le plus utilisé en récifal pour le réglage de l’intensité lumineuse.
Le pilotage se fait en signal "PWM" 0/10V ce qui n’est pas directement compatible avec l’Arduino, qui lui possède des sorties 0/5V.


ATTENTION :

le MeanWell ELN60-48D est réglable mais n’a pas d’entrée compatible "PWM", c’est une entrée analogique.


Avec le MeanWell, il n’est pas possible de faire varier l’intensité progressivement de 0 à 15%, c’est 0 puis 15 directement.
Si vous choisissez le MeanWell, il faudra réaliser une petite adaptation pour le connecter sur l’Arduino.
Vous aurez besoin alors des composants suivants :

- 2 résistances : 1kohm, et 150ohm ou 330ohms.
Pour une tension d’alimentation de 12 à 15V on choisit 150ohms, au delà 330Ohms.
- un transistor NPN BC547, 2N2222, 22369.
- une diode zener de 10V 0.5W.


Fournisseurs :
Dealextreme , Rapidled (pour le MeanWell) et Eb@y.

composants courants
(VPC, livraison rapide, port 4.70€) : gotronic
(VPC et Paris nation, fouilli mais très complet): ibcfrance
(VPC et 92) : electronique-diffusion


3) Réalisation Matérielle


Nous reprendrons comme base le montage du tuto Part.1 mais en ajoutant le driver Meanwell
Le schéma final est le suivant :



Résumé des connexions :

- Pour le LCD et le module RTC, voir le tuto Part.1

Le + du meanwell sur la cathode de la zener (repérée par la bande) et (la broche 1) de la résistance R1.
Le moins du Meanwell sur le collecteur du transistor (pate de droite pour un 2N2222).
La broche 2 de la résistance R1 sur "Vin" de l’Arduino.
L’anode de la zener et l’émetteur du transistor à la masse.
La résistance de 1K, une patte sur la base du transistor et l’autre sur la sortie "PWM" (ici la 11, de l’Arduino).

Explications du montage Zener / Transistor:

Le Meanwell a besoin d’un signal "PWM" 0/10V que l’Arduino ne peut fournir.
Nous utiliserons donc la tension d’alimentation du bloc secteur de l’Arduino.
Cette tension est présente sur sa sortie "Vin".
Pour que ce montage fonctionne, il faut que la tension d’alimentation soit supérieure à 10V,idéalement 12V sans dépasser 18V.


ATTENTION:


Un bloc secteur 12V ne délivre pas forcément 12V !!!

A vide (donc non branché et donc sans consommation), on peut monter à 15/16V! surtout si le bloc est mal régulé.
Ceci dit, si votre Arduino n’a pas encore grillé c’est que vous êtes en dessous de 18V. ;)
La tension de 10V est obtenue par le couple R1 et la zener.
Quelque soit la tension d’alimentation supérieure à 10V, la tension au bord de la Zener restera à 10V. Le complément sera aux bornes de la résistance.
Donc le MeanWell aura sur sa borne "+" une tension max de 10V.

Voici les calculs pour le choix des composants:

Pire cas possible :
"Vin" = 18V, donc R1=330ohms (voir plus haut).
Tension aux bornes de R1 : 18-10 = 8 Volts.
Courant Max disponible : I = UR1 / R1 = 8 / 330 = 0.024A soit 24 mA.
Puissance dissipée par R1 : P = U*I = 8 * 0.024 = 0.192W => un modèle 1/4W suffit.

On suppose le courant consommé par le MeanWell autour de 5 à 10 mA.
On choisit 5mA.
Puissance dissipée par la Zener : IZ = U * I = 10V * (0.024 – 0.005) = 10 * 0.019 = 0.19W => un modèle 1/2W suffit.

Conclusion :

Dans le pire des cas, pas de risque pour les composants.

Calculs pour une tension d’alimentation de 12V, (suivant les calculs ci dessus):

Courant max disponible avec R1=150ohms : 0.013A soit 13mA.
Puissance dissipée par R1 (150 ohms) = 0.026W
Puissance dissipée par la zener avec une consommation de 5mA par le MeanWell : 0.08W


4) Le programme

Nous reprenons le programme du tuto Part.1 qui se charge de récupérer l’heure toutes les secondes.
Il va falloir définir différentes valeurs d’éclairage dans le temps et en fonction de l’heure appliquer la bonne valeur à la sortie "PWM" (ici la "11" sur le schéma).

La plage de valeur se situe entre 0 et 255.
En sortie de l’Arduino, 255 est la valeur max et donc un signal 5V continu.
128 sera la moitié, donc un rapport cyclique de 50%.


ATTENTION:


Les Leds et certains drivers ne sont pas linéaires.

Cela signifie que si vous appliquez un rapport cyclique de 10%, vous n’aurez pas 10% de lumière!
Seul différents essais vous permettront de choisir les meilleures valeurs pour piloter le Meanwell.



Organigramme simplifié



On retrouve la fonction "Setup()" avec l'initialisation des différentes choses.
Par rapport au tuto Part.1, il va falloir paramétrer la sortie "PWM" justement en sortie.
C’est le cas par défaut, donc cela pourrait être omis mais autant le faire correctement.

Dans la fonction "loop()" on retrouve:
- la fonction "GetTime()" pour récupérer l’heure
- et "FlushLCD()" pour afficher les informations sur le LCD.

Il faut également rajouter l’appel à la fonction "SetPWMForLed()" qui est chargée d’envoyer la bonne valeur au driver Led en fonction de l’heure.
Cet appel ne sera effectué que toutes les minutes.
Inutile de le faire toutes les secondes, nous le verrons plus loin.

Principe pour le calcul de la valeur "PWM"

Le but est donc de commander entre 0 et 100% la rampe Led ceci en fonction de l’heure.
Il va donc falloir stocker les valeurs dans des variables.
Reste donc à définir le nombre de valeurs ?
Trop! on utilise de la place en mémoire, pas assez le réglage n’est pas assez fin.
Nous allons partir sur un tableau de 96 valeurs pour une plage de 24h.
Cela nous fait donc 4 valeurs par heure.
Pas assez ? nous calculerons ensuite des valeurs intermédiaire par moyenne.


Voici le tableau des 96 valeurs :
byte whiteled[96] = {
  0, 0, 0, 0, 0, 0, 0, 0,  //0 – 1h45
  0, 0, 0, 0, 0, 0, 0, 0,  //2 – 3h45
  0, 0, 0, 0, 0, 0, 0, 0,  //4 – 5h45
  0, 0, 0, 0, 0, 0, 0, 0,  //6 – 7h45
  1, 16, 32, 55, 80, 110, 140, 255,  //8 – 9h45
  255, 255, 255, 255, 255, 255, 255, 255,  //10 – 11h45
  255, 255, 255, 255, 255, 255, 255, 255,  //12 – 13h45
  255, 255, 255, 255, 255, 255, 255, 255,  //14 – 15h45
  255, 255, 255, 255, 255, 255, 255, 255,  //16 – 17h45
  255, 255, 255, 255, 255, 255, 255, 255,  //18 – 19h45
  110, 90,  70,  55,  40 ,  20,  10,   8, //20 – 21h45
  6  , 3 ,   3,   1,   1,    1,   0,   0  //22 – 23h45

Le tableau commence donc à 0h00 pour se terminer à 23h45.
Pour récupérer la valeur de 9h15, qui est ici 110, nous déterminons l’indice du tableau compris entre 0 et 95, de la manière suivante :

Indice = (heure*60 + minute) / 15

Ce qui nous donne : (9*60 + 15) / 15 = 37
Si nous regardons la 37ème position du tableau, il s’agit bien de la valeur 110.
De là, comme nous travaillons avec des chiffres entiers, ce n’est pas top.
En effet, à 9h15 j’ai une valeur de 110 et 9h30 la valeur suivante qui est 140.
Soit 30% en + d’un coup.
Il faut trouver un moyen de le faire en douceur toutes les minutes.

La réflexion est la suivante :

Indice de mon tableau: 37 = la valeur 110 (09h15).
Valeur de l’indice suivant: 38 = 140 (09h30).
L'écart est de 30 sur (15 minutes) entre 9h15 et 9h30.
Soit un incrément de 2 toutes les minutes.
En fonction de l’heure que j’ai récupérée, j’ajoute à la valeur 110 un incrément de 2/minute.

Exemple:
A 9h20, ma valeur d’éclairage sera:

((140-110) / (9h30-9h15)) * (9h20-9h15) + 110

Soit: 30/15 * 5 + 110 = 120.


15 sera une constante car fixe, en partant du principe que le tableau aura toujours 96 valeurs pour 24h.
Dans le cas où la valeur de l'indice est supérieure à la valeur de l'indice + 1, il faudra alors diminuer l’éclairage.
Nous utiliserons une fonction intermédiaire pour faire les calculs plus simplement.



Organigramme de niveau 2 de la fonction "SetPWMForLed()"



L’organigramme schématise les explications citées plus haut.
La fonction "SetPWMForLed()" appelle si nécessaire la fonction "average()" qui calcul une valeur intermédiaire entre la valeur de l’indice calculé et la valeur suivante.



A. La partie "include"

Pas de changement par rapport au tuto Part.1
#include <LiquidCrystal.h>
  #include <Wire.h>
  #include <RTClib.h>


B. La partie variable et alias

#define LCD_WIDTH 20
  #define LCD_HEIGHT 4
  #define ARRAYSTEP 15
  #define WHITE_LED 11
  char lcdbuf[LCD_WIDTH];




uint8_t DsHour ,DsMin ,DsSec;
  LiquidCrystal lcd(30, 31, 32, 36, 37, 38, 39);
  RTC_DS1307 rtc;
  int lcdw;


  byte whiteled[96] = {
  0, 0, 0, 0, 0, 0, 0, 0,  //0 - 1
  0, 0, 0, 0, 0, 0, 0, 0,  //2 - 3
  0, 0, 0, 0, 0, 0, 0, 0,  //4 - 5
  0, 0, 0, 0, 0, 0, 0, 0,  //6 - 7
  1, 16, 32, 55, 80, 110, 140, 255,  //8 - 9
  255, 255, 255, 255, 255, 255, 255, 255,  //10 - 11
  255, 255, 255, 255, 255, 255, 255, 255,  //12 - 13
  255, 255, 255, 255, 255, 255, 255, 255,  //14 - 15
  255, 255, 255, 255, 255, 255, 255, 255,  //16 - 17
  255, 255, 255, 255, 255, 255, 255, 255,  //18 - 19
  110, 90,  70,  55,  40 ,  20,  10,   8, //20 - 21
  6  , 3 ,   3,   1,   1,    1,   0,   0  //22 - 23
  };



Rappel :

Les lignes "#define" permettent d'associer dans le cas présent un Label à une constante.
Ici on définit/associe la largeur et la hauteur du LCD à 2 label que l'on utilisera plus tard.

Les label ou alias sont très pratique pour définir une broche.
Par exemple "#define LED_VERTE 34" permettra d'utiliser le Label LED_VERTE partout dans le programme pour commander votre Led connectée sur la broche 34 de l'Arduino.
Si, par la suite vous deviez changer la connexion de votre led sur une autre broche alors vous avez juste à redéfinir l'alias (inutile de modifier tout le programme).

De plus, un "#define" ne prend aucune place en mémoire pour le programme.
En effet, c’est le compilateur qui remplace les labels dans le programme par le valeur définit au début, ce que l’on appelle une directive de compilation.
Lorsque vous avez besoin d’une valeur qui ne changera jamais au cours du programme, préférez l’utilisation de "#define".

Les variables supplémentaires par rapport au tuto part.1 sont indiquées en rouge.

On définit une étiquette "LED_RAMP" qui a pour valeur 11, la broche de l’Arduino sur laquelle sortira le signal "PWM" de pilotage.
Lcdw sera utilisée pour stocker la valeur d’éclairage pour l’afficher sur le LCD ensuite en %.

C. La fonction "setup()"

Nous ajoutons la définition de la broche "WHITE_LED", à savoir la mettre en sortie.
Cela est le cas par défaut, cette ligne n’est donc pas utlie, mais autant le faire correctement :

void setup(void)
  {

  pinMode(WHITE_LED,OUTPUT);
. . . . . . .
}


Le reste ne change pas

D. La fonction "loop()"

void loop(void)
  {

GetTimeFromRTC();

  SetPWMForLed();

  Serial.print("Heure RTC : ");
  Serial.println(lcdbuf);

  FlushLCD();


Rien de méchant, on ajoute l’appel à "SetPWMForLED()"


E. La fonction "SetPWMForLed()"

void SetPWMForLed()
  {

  int indice, sstep, t1, t2,min_cnt,pwmmod ;

  Serial.println("Entrée SetPWMForLed");

  min_cnt= (DsHour*60)+ DsMin;

  // on calcule l'indice ((heure * 60) + minute) divisé par le pas du tableau
  indice = min_cnt/ARRAYSTEP;
     
  // Fonction modulo qui donne la partie décimale du calcul ci dessus (donc valeur entre 0 et le pas soit entre 0 et 15)  .
  sstep = min_cnt%ARRAYSTEP;
     
  t1 =indice;
      
  // cas où l'indice est le dernier du tableau, le suivant est donc le premier !
  if (t1==95) {t2=0;}
  // sinon indice suivant
  else {t2 = t1+1;}
  // on est tombé sur un indice entier (multiple de 1/4h), donc on envoie directement la valeur   
  if (sstep==0)
  {
  pwmmod = whiteled[t1];
  }
  else 
  {
  pwmmod = average(&whiteled[t1], &whiteled[t2], sstep);
  }
  
  Serial.print("pwmmod:");
  Serial.println(pwmmod);

  analogWrite(WHITE_LED, pwmmod);

  lcdw=pwmmod;

  }


Si vous avez suivi les explications ci dessus cela devrait être assez clair :

On calcul d’abord l’indice du tableau puis l’indice suivant.
On récupère les 2 valeurs d’éclairage et on en déduit une moyenne si nous sommes est entre les 2 et que ces 2 valeurs sont différentes.
A la fin, on envoie cette valeur sur la broche 11 avec la fonction "analogWrite" et on la stocke dans la variable lcdw pour plus tard.


F. La fonction "Average"

Cette fonction est appellée si on ne tombe pas sur une case du tableau mais si nous sommes est entre 2 valeurs.

// renvoie la valeur intermédiaire
  byte average( byte *pt1, byte *pt2, int lstep)
  {
    
    byte result;
    float fresult;
   
    // Les 2 valeurs des indices sont égales donc on ne change rien
    if (*pt1==*pt2) {result = *pt1;}   // Pas de changement
    
    // Cas 1 on augmente la luminosité
    else if (*pt1<*pt2)
    // Calcul de la valeur intermédiaire (cf Tuto)  
    { fresult = ((float(*pt2-*pt1)/15.0) * float(lstep))+float(*pt1);
     result = byte(fresult);
     }
    else 
    // Cas 2 on diminue la luminosité
    {
     fresult = -((float(*pt1-*pt2)/15.0) * float(lstep))+float(*pt1);
     result = byte(fresult);
    }

    return result;
  }


Notez la déclaration de la fonction "byte average" et non "void average".
Cela indique que la fonction prendra une valeur de type byte en retour.
On compare les 2 valeurs passées en paramètre et en fonction, l'on calcule la valeur intermédiaire à l’aide du troisième pas qui représente le modulo de l’indice calculé au début de "SetPWMForLed()".

Le programme complet est: ICI

G. La fonction "FlushLCD"

Nous affichons en plus la valeur d’éclairage à l’aide de la variable lcdw.

void FlushLCD()
  {

  lcd.setCursor(0,1);

  // Ex: Blanc : 54%
  sprintf(lcdbuf, "Blanc : %d%%",lcdw*100/255);
  lcd.print(lcdbuf);

  lcd.setCursor(0,0);

  sprintf(lcdbuf, "%.2d:%.2d:%.2d",DsHour,DsMin,DsSec);
  lcd.print(lcdbuf);

  }

sprintf permet pour rappel de formater l’affichage de la variable, ici en valeur entière.


5) Le Test

Si tout est bien fait vous devriez vous retrouver avec votre Rampe Led éclairée suivant le pourcentage désiré.

Pour rappel et ci cela ne fonctionne pas :

- Vérifier les connexions des différents éléments.
- Vérifier l'alimentation de l'Arduino
- Se lancer dans le debug du programme.
Pour celà vous pouvez ouvrir la fenêtre Serial Monitor du programme Arduino sur l'ordinateur et mettre des "Serial.println" partout où bon vous semble dans votre programme.
Ainsi vous pourrez suivre son déroulement facilement.


6) Améliorations et prise de conscience :)

Nous avons rajouté dans la fonction "loop()" l’appel à "SetPWMForLed" pour régler l’éclairage de la rampe Led!
Seulement voilà, l’appel est réalisé toutes les secondes ce qui est totalement inutile.
Appeler la fonction toutes les minutes serait largement suffisant.

Pour cela, nous appellerons la fonction "SetPWMForLed" uniquement si elle n’a pas été appellée depuis plus d’une minute.

Comment ? :blink:

Il suffit simplement de stocker dans une variable l’heure d’appel de la fonction et ainsi de le comparer à l’heure courante.
Nous allons utiliser pour cela la fonction "millis()".
Cette fonction renvoie le nombre de millisecondes écoulées depuis le début du programme, donc depuis que l’Arduino a été allumé ou depuis qu’il a rebooté.
On rajoute en début de programme une variable pour stocker la valeur de la fonction "millis()":
unsigned long LastTimer ;

Puis à la fin de la fonction "setup" on initialise cette variable avec "millis()" et on appelle derrière la fonction "SetPWMForLed":
LastTimer = millis() ;
  SetPWMForLed() ;


On modifie la fonction "loop" pour l’appel de "SetPWMForLed" :
// 1 minute écoulée depuis le dernier appel ??
  if ((millis() - LastTimer) > 60000)
  {
  SetPWMForLed();
  LastTimer=millis();
  }


Dans la fonction "Loop()", on compare la valeur actuelle de "millis()" avec la valeur stockée.
S'il y a une différence de 60.000 miliseconde, soit 1 minute, on réinitialise la variable avec la valeur actuelle de "millis()" et on appelle la fonction "SetPWMForLed()" !

Si vous regardez le test lors du premier appel après le démarrage, la valeur de "millis() – LastTimer" est proche de 0 et il faudra donc attendre quasiment 1 minute avant le premier appel de la fonction "SetPWMForLed()".
C’est pour cela que nous l’avons appellée une première fois à la fin de "Setup()"afin de pouvoir piloter la rampe dès le démarrage de l’Arduino et ne pas à avoir attendre 1 minute.


Limitations de "millis()"

"millis()" représente un compteur qui s’incrémente sans cesse.
Seulement voilà ce compteur n’est pas infini et retournera à 0 tôt ou tard.
Il faut garder en tête que ce compteur retournera à zéro tous les 53 jours environ.

Regardons notre code :

Au bourt de 52 jours "millis()" aura une valeur proche de 0 et "LastTimer" une valeur proche de la valeur maximale.

Soit 52 * 24 * 60 * 60 * 1000 = 4 492 800 000.

Donc notre test ne fonctionnera plus et il faudra attendre avec un peu de chance 52 jours de plus pour piloter de nouveau la rampe Led.
Là ça chipote un peu.:P
Mais nous allons faire en sorte que le test fonctionne tout le temps.
L’explication est sur ce site (en anglais) mais utilisent l’astuce des variables signées :

www.arduino.cc/playground/Code/TimingRollover

Nous allons donc comparer la valeur de millis convertie (« castée ») en type long.
Quelque soit la valeur actuelle de "millis()" et de la variable

Exemple:
millis = 10 et LastTimer = 33 000 000, le test fonctionnera toujours.
Je vous laisse voir l’article pour les explications.

Modification du code :

Déclaration :
static unsigned long LastTimer ;


Dans "setup()" :
LastTimer  = 0;
  SetPWMForLed() ;


L’appel initial à "SetPWMForLed" n’est plus nécessaire et il sera fait dès la première fois dans "loop()"


Le test dans loop() devient :
if( (long)( millis() - LastTimer ) >= 0)
  {
  SetPWMForLed();
  LastTimer+= 60000;
  }


Le but ici n’est pas de faire du code propre mais de bien connaître les limites d’une fonction (native Arduino ou non).
D’imaginer tous les cas possible et de se poser les bonnes questions.
Avec un peu de pratique de l’Arduino, cela viendra tout seul ;)


Le programme complet est : ICI


7) Conclusion

La gestion de la rampe est faite.
Besoin de piloter une rampe bleue en plus par exemple ?
Il vous faudra un autre tableau et ainsi modifier l’intérieure de la fonction "SetPWMForLed()" en doublant les test ou mieux, faire en sorte que cette fonction prenne en paramètre le bon tableau! la bonne sortie à piloter et l’appeler 2 fois.
Une pour la rampe blanche et une pour la bleue.
Reste à déterminer les bonnes valeurs en fonction de l’aquarium, de la population et du biotope.
Ce tuto part.2 permet en tout cas à l’Arduino de gérer l’éclairage, fonction primordiale pour votre récif.
De très nombreuses autres possibilités sont offertes mais ce fera l’objet d’autres tutos à venir

Bon amusement :)

8) Renvoi vers le post commentaires de la rubrique DIY

Post Part.2 de The_ccm
Qu''importe le flacon pour vu qu'on ait l'ivresse...
Dernière édition: 23 Déc 2011 08:47 par unjall.
Le sujet a été verrouillé.

Re: ARDUINO by The_ccm Part.3 04 Juil 2012 14:49 #339533

  • unjall
  • Portrait de unjall
  • Hors Ligne
  • Modérateur
  • Messages : 6585
  • Remerciements reçus 124
1) Descriptif du projet

Programme n°3 : "Contrôle par Télécommande Infra rouge"

Troisième tutoriel qui va permettre d'interagir plus facilement avec l'Arduino.
J'ai étudié les différentes façons d'envoyer des ordres tout en cherchant la plus simple niveau programmation et intrinsèquement la plus rapide, mais aussi la plus facile à mettre en œuvre matériellement.

Parmi les solutions possibles :

a) le clavier :

Solution qui vient le plus rapidement à l'esprit et peu chère, un clavier coutant une dizaine d'euros.
Simple car en général peu de fil a câbler et également simple a programmer.

Je me suis ensuite posé tout de suite la question de l'intégration du clavier et c'est là que les problèmes commencent.
Les solutions toute faites sont limitées en référence et souvent on nous vend le clavier sans le boîtier, ou alors c'est du tout intégré avec le LCD sur la même platine.
Je n'ai donc pas cherché plus loin.

b) les claviers capacitifs :

On garde le principe du clavier mais les interrupteurs ne sont plus mécaniques mais composé d'une plaque de cuivre classique avec 2 pistes dessinées spécialement et que l'on place dans le boîtier sous la face avant à quelques millimètres.
En approchant le doigt, on créer une capacitance différente de celle des pistes et on peut détecter en sortie un état zéro ou un.
- Inconvénient: on détecte l'état avec plus ou moins de réussite, on est en présence de signaux analogiques et donc un calibrage doit se faire.

Quelques exemples sur le net dont sur Arduino playground si toutefois vous êtes tentés :
Arduino Playground : CapSense

c) La communication wireless ou réseau :

Solution qui peut répondre à tous les besoins, cela peut être une interface réseau filaire, un module Bluetooth ou wireless.
C'est cela dit plus difficile à mettre en œuvre et il faut un Pc à proximié, pas top.

d) L'infra rouge :

On reste dans le wireless mais beaucoup plus simple.
On utilise une télécommande, universelle ou non d'une par, et d'un capteur infra rouge d'autre part.
C'est très simple à mettre en oeuvre.
Matériellement le capteur contient trois fils et le programme est très simple, pourvu que l'on dispose de la librairie adéquat.
C'est donc cette solution pour laquelle j'ai opté


Le Cahier des charges :

- L’arduino doit pouvoir répondre à n'importe quelle télécommande.
- Gérer une dizaine de touche au minimum( menu, flèches, retour, entrée, et les chiffres) par exemple pour un pilotage direct des sorties rélais.
- Coût inférieur à 10€.


Le programme cible pour exemple :

- Piloter les relais de sortie
- Gérer un mode feed c'est à dire nourrir la population de l'aquarium.


2) Préparatifs


Difficulté : ♠♠
Temps nécessaire : environ 1h pour le câblage et 1h pour le programme.
Coût du projet et liste du matériel :
les mêmes composants que pour le tuto Part.1, à savoir:

- Une platine Arduino Uno, Duemilanove ou Mega, je recommande toujours cette dernière pour environ 5 à 10€ de surcoût.
- Un afficheur LCD.
- Un module horloge RTC.
Pour le choix des composants voir le tuto Part.1

En supplément :

- Un capteur Infra-rouge 38Khz, par ex. TSOP1738. Datasheet à cette adresse
- Une carte relais :


C'est une carte classique que l'on peut trouver partout.
Chaque broche commande un relais, il est donc très facile de piloter les sorties, indépendamment les unes des autres.


Quelques notes techniques:

Le capteur utilisé est des plus largement répandu, j'ai d'ailleurs trouvé le mien sur un vieux démodulateur satellite de recup, la ref est tsop 1738. N'importe quel récepteur infra rouge 38khz ayant une sortie TTL (5V) peut convenir.

Il y a seulement 3 fils, alimentation comprise!
Donc +5v, masse et sortie des données.
Pour un détail du fonctionnement d'une transmission IR, c'est ici.

La fréquence utilisée sera celle des 38khz, c'est une des plus répandues pour les télécommandes.

Le choix de la télécommande

Une télécommande universelle est idéale, le codage est standardisé.
Une télécommande dite préprogrammée par marque (les moins chères) convient très Bien.
Une télécommande existante est déconseillée car vous risquer d'éteindre les pompes en voulant changer de chaîne, ou alors il faut prendre des touches inutilisées par la TV.


3) Réalisation Matérielle


On reprend le schéma du TUTO 2, voir le 1 sachant que l’on n’utilisera pas le meanwell dans notre exemple.

Rien d'exceptionnel, le capteur Infra-rouge à été ajouté. Celui-ci possède donc 3 fils, dont 2 sont utilisés pour l'alimentation en 5V. Le troisième est donc le fil de Data.
Une résistance de 100ohms peut être insérée sur le fil 5V en accord avec le datasheet afin d'éviter les parasites sur de grandes longueurs.


Voici le schéma (cliquez pour agrandir) :



Les données du capteur seront donc reçues sur la broche 17.


4) Le programme


Nous allons gérer dans l'ordre :

1) Ce que j'appelle le feed mode: en appuyant sur une touche de la télécommande, on arrête toutes les pompes, l'osmolateur et filtre pour permettre de nourrir les coraux, les poissons....
En même temps on détermine l'heure de fin.
Ensuite on compare systématiquement l'heure système avec l'heure de fin, quand on la dépasse on remet en route tout ce qui a été arrêté.

2) l'arrêt et la mise en marche indépendante des relais.
Touche 1 pour le relai 1, etc. Jusqu'à 4.
Un appui inverse l'état du relais.

3) gestion d'un menu avec la télécommande : à titre d'exemple le menu permettra de régler le temps du Mode nourriture.
Le menu est appelé avec la touche "flèche haut", la sortie de ce menu se fait avec la touche "Retour" ou "Exit"


L'organnigramme détaillé





Rien de compliqué dans le loop(). On boucle tout le temps et :
-si on reçoie une donnée du capteur Infra-Rouge, on se branche sur la fonction GetIRKey().
-si on est à la fin du "FeedMode", on se branche sur la fonction pour commuter les relais.

On s'occupera aussi dans le loop de récupérer l'heure du module RTC, ainsi que l'affichage sur le LCD.


Test de reception des données


Pour réceptionner les données du capteur infra-rouge, on utilise une librairie qui va tout faire à notre place ou presque.
Il s'agit à la base d'une librairie utilisée dans un montage Arduino faisant office de passerelle freebox infrarouge vers ethernet pour commander celle ci.
Nous allons reprendre 95% du programme.

Le fonctionnement de la librairie est dit sur interruption, c'est à dire que l'Arduino est capable d'exécuter votre programme classique d'une part, et en même temps gérer la réception des données sur le capteur.

Ainsi il n'est pas nécessaire dans le programme principal de regarder la broche du capteur en permanence et voir si on reçoit quelque chose, la librairie se charge de récupérer les données elle même et de positionner une variable lorsque la donnée reçue est complète.
Seul bémol, pour le moment, il faudra tester la valeur de cette variable dans le programme principal afin de savoir si on a reçu quelque chose ou non, c'est le test qui est représenté sur le diagramme ci-dessus.


Test de fin du FeedMode


Nous allons avoir besoin de 2 variables :

- La première représentera l'heure système courante et sera mis à jour régulièrement par l'appel sur le module RTC.
- La deuxième représentera l'heure de fin de feedmode.

Comment représenter dans une variable la notion de jour, mois, année, heure, minute et secondes ?
Il existe un format qui s'appelle le format unix timestamp, et qui représente le nombre de secondes écoulées depuis le 01/01/1970 à minuit GMT. Celà peut paraître compliqué à gérer mais certaines fonctions vont nous aider.

Le point positif est que comparer 2 variables dans ce format est très simple, ainsi pour comparer si le 29 Mars 1999 08h30 est supérieur au 03 Janvier 1980 16h40, un simple "if (var1>var2)" suffit!
En revanche, comment convertir les différentes valeurs reçues du module RTC dans une seule variable ? C'est ici que rentre en jeu la librairie DateTime.


La librairie Datetime


On utilisera 2 parties de cette librairie :
tout d'abord elle est capable de récupérer les données du module RTC, ainsi les autres librairies de gestion RTC, telles que DS1307RTC ne sont plus nécessaires et doivent être supprimées.
Enfin elle permet de faire la conversion entre le format Unix TimeStamp et les différentes valeurs jour, mois, année etc.

Réprésentation du format Unix Timestamp

Comme dit plus haut, la valeur est stockée dans une seule variable de type unsigned long, elle peut donc prendre en charge une valeur entre 0 et 2^32 secondes (4 294 967 296), pour rappel la valeur 0 est le 01/01/1970 00:00 GMT, et la valeur 4294967296 représente le dimanche 7 Février 2106 à 06h28m16s précisément. Au delà, le programme ne fonctionnera plus :) donc ça laisse venir

Représentation du format "jj/mm/aaaa hh:mm:ss"

Pour représentrer la date et l'heure sous un format standard, nous utilisons une structure. Une structure n'est rien d'autre qu'un ensemble de variables dont l'accès est simplifié. C'est un peu comme si au lieu de déclarer 3 variables de type entier nous déclarons un tableau de longueur 3 de type entier.

Dans la librairie DateTime, la struture pour stocker la date et l'heure est la suivante :
typedef struct  {
  uint8_t Second;
  uint8_t Minute;
  uint8_t Hour;
  uint8_t Wday;   // day of week, sunday is day 1
  uint8_t Day;
  uint8_t Month;
  uint8_t Year;   // offset from 1970;
}  tmElements_t

Ici nous inventons donc un type tmElements_t qui est notre structure avec à l'intérieur les variables qui nous intéresse. Pour déclarer une variable de ce type, celà ce fait comme n'importe quel autre type :
tmElements_t TimeVar;

et pour lire ou modifier les différents éléments, par exemple :
TimeVar.Hour =23;
TimeVar.Minute = 59:

AutreVariable = TimeVar.Month;

Utilisations des 2 formats

Le stockage de sous la forme d'une unique variable unsigned long sera utile pour tous les calculs dans notre programme, comparaison entre 2 dates/heure, additions et soustractions, etc.

Le stockage sous forme de structure sera utile principalement pour l'affichage sur un LCD, pour la sortie Debug, bref pour l'affichage.

Conversion entre les 2 formats

Il est très simple de transformer un format vers l'autre, c'est d'ailleurs là que réside l'intérêt de la librairie.
2 fonctions sont implémentées :
MakeTime pour convertir du format structure vers le format unsigned long
BreakTime pour convertir du format unsigned long vers le format structure


Entrons dans le vif du sujet.


Les librairies utilisées

#include <Time.h>  
#include <LiquidCrystal.h>
#include <Arduino.h>
#include <Wire.h>
#include <IRremote.h>
#include <DS1307RTC.h>

Il faut inclure notre nouvelle librairie Datetime, ainsi que la librairie IRRemote pour gérer le capteur infrarouge (liens en fin d'article). DS1307RTC se charge de récupérer l'heure du module horloge au format Timestamp, Wire est classique pour la gestion du bus I2C, Arduino.h est la librairie nouvelle version de l'environnement de développement v1.0
Enfin LuiquidCrystal elle aussi est classique pour gérer l'afficheur 4x20.


Les constantes

pour rappel un Define ne prend pas de place en mémoire dans l'Arduino, c'est uniquement un alias remplacé par sa valeur dans le programme au moment de la compilation.
#define IR_SENSOR 17

#define LCD_WIDTH 20
#define LCD_HEIGHT 4

#define RLY_ECUMEUR 3
#define RLY_FILTER 4
#define RLY_POMPE2 5
#define RLY_POMPE1 6

IR_SENSOR sera la broche du capteur infra-rouge, LCD_X indique la largeur et hauteur du module LCD, et les alias RLY_X les broches où sont branchés les relais, rien de bien méchant.


Les variables globales
// Dans l'ordre : RS  R/W EN D4 D5 D6 D7 
LiquidCrystal lcd(30, 31, 32, 36, 37, 38, 39);

// Zone tampon pour afficher l'heure sur la sortie Série et LCD
char filebuf[LCD_WIDTH];

// Init capteur IR
IRrecv irrecv(IR_SENSOR);


time_t RTCTime;
tmElements_t RTCtm;

time_t FeedEndTime;
time_t FeedDuration;

int MenuMode;

filebuf est une variable de type caractère, de longueur LCD_WIDTH, donc la largeur de l'afficheur. On l'utilisera pour formater notre heure et utiliser directement cette variable à l'affichage.

irrecv est l'objet gérant le module IR et contiendra la valeur de la touche reçue.

4 variables pour gérer 2 valeurs, l'heure courant et l'heure de fin de FeedMode. Pour chaque valeur on a les 2 formats de stockage, le format unix et le format structure avec les éléments temps séparés.

Enfin MenuMode sera utile pour savoir dans quel état est l'affichage, mode normal avec affichage de l'heure, ou mode paramétrage pour la durée du FeedMode.


L'initialisation
void setup(void) 
{

  // Toujours garder une trace du reboot pour ceux qui ne seraient pas voulus !!
  Serial.begin(57600);
  Serial.println("Reboot!!");
  
  // Init circuit I2DC
  Wire.begin();

  lcd.begin(LCD_WIDTH, LCD_HEIGHT,1);
  lcd.noCursor();
  lcd.noBlink();

  
  FeedEndTime=0; // Mode Feed non actif par défaut

  FeedDuration=SECS_PER_HOUR * 2; // Par défaut 2h pour le FeedMode

  // Demarage du recepteur IR
  irrecv.enableIRIn(); 

  MenuMode=0;

}

Première chose à des fin de debug, l'init. du port série et un petit message pour dire que l'on reboote, celà peut faire gagner des heures de debug, où on cherche à comprendre ce que fait l'Arduino alors qu'avec ce message on voit tout de suite que le programme reboucle sans cesse pour cause de typage de variable mal géré, débordement, etc.

Ensuite l'initialisation du bus I2C avec Wire.begin() et ensuite celle du module LCD, à noter que noblink et noCursor ne sont pas obligatoires.


Vient l'initialisation de FeedEndTime, que l'on met à 0, celà permet de savoir ensuite que le mode FeedMode n'est pas actif.

FeedDuration est initialisé à 7200 secondes, soit 2h, notez au passage l'utilisation de constantes de la librairie DateTime.


On active ensuite le module IR avec une de ses propriétés enableIRIn()

Enfin on mets MenuMode à zéro, pour indiquer que l'on est dans le mode d'affichage classique, à savoir l'heure courante et l'heure de fin de FeedMode si celui-ci est actif.


Récupération de l'heure
void GetRTC()
{
   
 RTCTime=RTC.get();  // Format Unix TimeStamp
  
 breakTime(RTCTime,RTCtm); // Structure RTCtm avec propriétés Heures, minutes ... etc.
 
 // Pour formattage  
 sprintf(filebuf, "%.2d:%.2d:%.2d",RTCtm.Hour,RTCtm.Minute,RTCtm.Second);

 Serial.print("Heure courante:");
 Serial.println(filebuf);
  
   
}


GetRTC()
est appellée à chaque passage dans le loop(). RTC.get() récupère l'heure courante depuis le module, ensuite breaktime envoie l'heure récupérée dans le premier paramètre, et renvoie en sortie l'heure au format structure définit plus haut, ici en l'occurence dans la variable RTCtm.

Puis vient la très intéressante fonction sprintf. Voir le premier Tuto pour les explications, mais elle permet de formater les variables toujours de la même manière, ici c'est utile par exemple sur les minutes pour mettre automatiquement le "0" si les minutes sont comprises entre 0 et 9.

Un coup de Serial.println pour vérifier que l'on récupère bien l'heure.

Le Mode Feed
void FeedUs()
{
  
  FeedEndTime = FeedDuration;
  FeedEndTime += RTCTime;

  // Ici la valeur HIGH coupe le relais (logique inversée !)
  digitalWrite(RLY_ECUMEUR,HIGH);
  digitalWrite(RLY_FILTER,HIGH);
  digitalWrite(RLY_POMPE1,HIGH);
  digitalWrite(RLY_POMPE2,HIGH);

}

Cette fonction sera appellée par le bouton Feed de la télécommande. Le code est assez explicite, on calcule l'heure de fin de ce mode, ici FeedEndTime est calculé en 2 fois, on l'initialise d'abord avec l'heure courante, et on ajoute le contenu de FeedDuration, 7200 (secondes) par défaut si on ne l'a pas modifié par le menu paramétrage.

Ensuite on coupe tous les relais que l'on souhaite, ici il peut y en avoir 2,3,4,8,16... autant qu'on veut.


Inversion des sorties relais
int ToggleRelay(int relaypin)
{

  int relaycurval;

  relaycurval=digitalRead(relaypin);


  if (relaycurval==HIGH)
  {
    digitalWrite(relaypin,LOW);
  }
  else
  {
    digitalWrite(relaypin,HIGH);
  }
 
}

Appellée par les touches d'accès directes 1,2,3 ou 4 de la télécommande. Le numéro de broche est passé en paramètre, on lit l'état de la broche et on l'inverse pour le renvoyer à ladite broche.

On notera au passage que ceci est l'implémentation compréhensible de la fonction d'inversion, sachez que ce code peut s'écrire en une ligne, mais celà fera l'objet d'un prochain tuto "optimisation du code" :)


Récupérer la valeur du capteur IR
void GetIRKey()
{
  
decode_results results;	

if (MenuMode==0)
  {
  //MenuMode=0, Mode standard, on n'est pas dans le menu LCD
  if (irrecv.decode(&results)) 
    {
   Serial.print("Reçu:");
   Serial.println(results.value);
 
   switch (results.value) 
     {
         // Touche Flèche Haut
         case 10221: MenuMode=1;Menu(); break;
         // Touche 1 (Ecumeur)
         case 237: ToggleRelay(RLY_ECUMEUR);break;
         // Touche 2 (Filtre)
         case 16621: ToggleRelay(RLY_FILTER);break;
         // Touche 3 (Pompe 1)
         case 8429: ToggleRelay(RLY_POMPE1);break;
         // Touche 4 (Pompe 2)
         case 24813: ToggleRelay(RLY_POMPE2);break;
         // touche 0 (mode Feed)
         case 18669: FeedUs(); break;
         
     }
   
   // Tempo pour eviter la repetition d'une commande non voulu
   delay(200); 
   irrecv.resume();  // reprise de la reception IR, necessaire

  }
 }
 else
 {

  //MenuMode=1, Mode Menu LCD
  if (irrecv.decode(&results)) 
    {
     switch (results.value) 
       {
       case 1261 :  FeedDuration+=60;break;
       case 17645 :  FeedDuration-=60;break;
       case 25581 :  MenuMode=0;break;
       }  

     irrecv.resume();  // reprise de la reception IR, necessaire
     Menu();
   
    }

  }


}

un gros if sur la valeur de la variable MenuMode :
si "0", on est dans l'affichage normal, on gère les touches directes et l'appel du menu de paramétrage
si "1", on est déjà dans le menu de paramétrage, et on gère uniquement les touches pour régler la valeur de la variable FeedDuration

on notera le test sur la propriété de l'objet IR, irrecv.decode, qui est un booléen et permet de savoir si on a reçu un signal de la télécommande, si le booléen est faux, on n'a rien reçu, on ne fait rien.

ensuite nous testons les valeurs reçues avec un switch, qui remplace une succession de if, et suivant les touches appuyées on active la fonction désirée.

Dans le cas de MenuMode=0, c'est assez simple. on notera que l'appui sur la touche "flèche haut", change la valeur de MenuMode et appelle le menu de paramétrage.

Dans le cas de MenuMode=1, on jouera uniquement sur la valeur de FeedDuration (+ ou -) et on changera la valeur de MenuMode à 0 si on appuie sur "Retour".




Le menu de paramétrage
void Menu()
{

  Serial.println("Entrée Menu()");

  lcd.setCursor(0,0);
  lcd.print("   Duree Mode Feed");
  lcd.setCursor(0,1);
  lcd.print("   ***************");
  lcd.setCursor(8,3);
  sprintf(filebuf, "M",FeedDuration);
  lcd.print(filebuf);
  
  GetIRKey();

}

L'affichage du paramétrage n'a rien de compliqué. On affiche uniquement la valeur de la variable FeedDuration. On rappelle ensuite la fonction GetIRKey() vue juste au dessus, comme MenuMode=1 , on attends un appui sur les touches "+", "-" ou "Exit". on reboucle ensuite sur l'affichage.

On voit donc que l'on est dans une boucle sans cesse, tant que l'on a pas remis MenuMode à 0, c'est à dire appuyé sur la touche "Exit".

Autrement dit, si la touche "Exit" est mal gérée, nous sommes dans une boucle sans fin ! on ne reviendra jamais au menu principal, on ne récupère plus l'heure actuelle, pour peu que l'heure commande un éclairage Led dans un programme plus élaboré, on voit tout de suite les dégats.

Le fait de faire un organigramme détaillé du programme permet d'éviter de faire ce genre d'erreur, couplé à des debug sur la sortie Serial, c'est encore mieux.


Le loop/b]
void loop(void) 
{
  

  GetRTC();   
  	
  if ((RTCTime>FeedEndTime) && (FeedEndTime!=0))
	{
        FeedEndTime=0;
        // Ici la valeur LOW active le relais (logique inversée !)
	digitalWrite(RLY_ECUMEUR,LOW);
        digitalWrite(RLY_FILTER,LOW);
        digitalWrite(RLY_POMPE1,LOW);
        digitalWrite(RLY_POMPE2,LOW);
	}


  if (MenuMode==0) {Affiche();}

  GetIRKey();
  delay(400);
  GetIRKey();
  delay(400);

}

on récupère l'heure avec GetRTC vu plus haut. on détecte ensuite si le feedMode est actif (FeedEndTime<>0) et si l'heure de fin du mode est supérieure à l'heure courante, comme je disais plus haut une simple comparaison entre les 2 variables suffit ! Si on a dépassé l'heure de fin, on réactive les relais, on remet FeedEndTime à 0, pour désactiver le mode.

Vient ensuite l'affichage principal, uniquement si MenuMode=0, donc si on n'est pas en mode paramétrage. on appelle GetIRKey() 2 fois de suite à intervalle régulier pour voir si on a reçu un signal de la télécommande.


5) Programmation de la télécommande

je vous renvoie au lien de bas de page (PDF) qui indique la marche à suivre, l'auteur l'a très bien expliqué.

programmation de nouveaux code
Vous noterez que les valeurs sont en dur dans le code et correspondent à la configuration du PDF. Si vous devez gérer un autre type de télécommande (Apple TV, Télécommande originale,etc), je vous suggère de faire un programme tout simple qui appelle GetIRKey() et qui envoie sur la sortie Serial la valeur de chaque touche appuyée, de les notez, pour les recopier ensuite dans ce programme.

6) Conclusion

Voici un programme qui reste simple, avec de nombreuses possibilités.

Les avantages :
- possibilités de gérer de nombreux ordres si on dispose de beaucoup de touches.
- Simple, rapide et efficace
- Peu cher et facile à mettre en place techniquement

Inconvénients :
- lorsque l'on est dans le menu de paramétrage, on ne gère rien d'autre.
- obligation de tester en permanence l'état du capteur IR pour savoir si on a reçu un ordre et si le l'objet IR contient la valeur.


7a) Liens librairies, programme et documentation

Le programme de ce tuto

le fichier pdf de la librairie IR et la configuration de la télécommande

Librairie IRRemote


Librairie DS1307

Librairie DateTime


7b) Renvois vers le post commentaires dans la rubrique DIY

post Arduino part.3
Qu''importe le flacon pour vu qu'on ait l'ivresse...
Dernière édition: 04 Juil 2012 14:53 par unjall.
Le sujet a été verrouillé.
Modérateurs: unjall
Temps de génération de la page : 0.470 secondes

Une bouteille à la mer

billyboyk - 17:19

20 kilos de PV avec du mou, ça tiendra 2 jours dans un bidon brassé/chauffé ;)

billyboyk - 08:22

:D Bonne année à tous !!! xD

habaqouq - 22:06

bonsoir comme insérer une image dans un post merci

delphizorglub - 18:08

Ouaou :p super

babe91800 - 12:18

Bonjour à tous :) petit come back après un moment d'absence

The shoutbox is unavailable to non-members