Afficheurs Circuit Temporel Retour vers le Futur

Bonjour à tous, il y a quelques mois je me suis lancé dans ce projet. J'ai effectué quelques modifications pour coller au mieux à l'afficheur que l'on voit dans le film. (afficheurs AlphaNum, affichage spécifique des chiffres 6 et 9, création de PCBs notamment)
N'hésitez pas à jeter un oeil à ma page Facebook (Facebook) pour avoir plus d'infos.

Le montage de base fonctionne bien.
Cependant, je voudrais ajouter un clavier pour choisir la date de Destination, comme dans le film.
Malheureusement, je bloque.
Pour info, le montage fonctionne avec un Pro-Mini 5V/16MHz et j'ai un UNO pour les tests.
J'utilise la librairie OneWireKeypad, je dois utiliser le moins de broches possible car au final je veux ajouter un WaveShield pour les effets sonores et celui-ci demandes un nombre important de broches.
Bref, là n'est pas le problème pour l'heure.

Le montage du clavier est fonctionnel après test des 2 sketchs-exemple fournis avec la librairie.

Mon problème est le suivant:
-Ajouter une sous-fonction dans le LOOP qui se charge de lire le clavier et de stocker les infos de touches dans un tableau. (Appelons-la saisieDate())
-Utiliser les valeurs de ce tableau dans une autre sous-fonction qui est déjà dans le LOOP. En l'occurence, displayDate().

Je vous mets mon code de base en Pièce Jointe.

Pour l'ajout du clavier, je compte procéder comme suit:
-Paramètres propres à la librairie OnewireKeypad:

#include <OnewireKeypad.h>

char TOUCHES[]={
'1','2','3',
'4','5','6',
'7','8','9',
'*','0','#'
};

OnewireKeypad <Print, 12> Clavier(Serial, TOUCHES, 4, 3, A0, 4700, 1000);

Dans le void SETUP:

Serial.begin(115200);
Clavier.addEventKey(saisieDate, '*'); //accès à la sous-fonction saisieDate via la touche '*'

Dans le LOOP:

Clavier.ListenforEventKey(); //contrôle si la touche '*' est pressée

-Paramètres à modifier dans le programme de base:
Passer de:

// Configuration Fonction DateTime pour les objets dest et last
DateTime dest(55, 11,  5, 22,  4, 100+19), // Nov 5, 1955 10:04 PM Objet dest (futur)
         last(85, 10, 26,  1, 24, 100+19); // Oct 26, 1985 1:24 AM Objet last (passé)

à:

// Configuration DateTime dest et last
byte d_annee = 55;
byte d_mois = 11;
byte d_jour = 5;
byte d_heure = 22;
byte d_minute = 4;
byte d_siecle = 19;
byte l_annee = 85;
byte l_mois = 10;
byte l_jour = 26;
byte l_heure = 1;
byte l_minute = 24;
byte l_siecle = 19;

DateTime dest(d_annee, d_mois, d_jour, d_heure, d_minute, 100+ d_siecle), // Nov 5, 1955 10:04 PM
         last(l_annee, l_mois, l_jour, l_heure, l_minute, 100+ l_siecle); // Oct 26, 1985 1:24 AM

C'est à partir de là que je sèche...
Comment récupérer les données de touches via la sous-fonction saisieDate et les affecter aux variables d_annee, d_mois, d_jour, etc... pour les ré-utiliser dans la sous-fonction displayDate?
Utiliser la fonction Clavier.Getkey() est une évidence mais pour le reste...

Pour le moment, les variables l_annee, l_mois, etc... ne changeront pas. C'est juste en prévision pour une future évolution du projet.

Merci d'avance pour votre aide.

Time_Circuit_BTTF.ino (6.81 KB)

2,21 gigawatts dans un arduino ?? :o

bricoleau:
2,21 gigawatts dans un arduino ?? :o

Nom de Zeus!! Lol

Voici comment je procéderais, sur le principe :

void loop()
{
  gererClavier();
  gererAfficheurs();
}

gererClavier() lit les éventuels événements claviers et met à jour des variables globales.
Ces variables globales permettent de définir l'état du système à un instant t

gererAfficheurs() pilote les afficheurs en fonction du contenu des variables globales

Par exemple, juste pour illustrer : l'appui sur la touche * fait entrer dans le mode "saisie d'une nouvelle date".
Dans ce cas, l'année doit être effacée et au niveau du chiffre des unités on doit afficher une barre clignotante, en attendant une saisie de chiffre.

bool saisie_en_cours = false;
char chiffres[4];
byte nb_chiffres = 0:

void gererClavier()
{
   ...
   if (clav == '*')
   {
     saisie_en_cours = true;
     nb_chiffres = 0;
   }
   else if ('0' <= clav && clav <= '9' && saisie_en_cours && nb_chiffres < 4)
   {
     chiffres[nb_chiffres++] = clav;
   }
   ...
}

void gererAfficheurs()
{
  if (saisie_en_cours)
  {
    //gérer les affichages en fonction du contenu des variables "chiffres" et "nb_chiffres"
  }
}

Merci pour ces infos.
Dans mon cas les variables globales sont: d_annee, d_mois, etc...
Si je suis ton raisonnement, je me sert de ma sous-fonction saisieDate pour lire le clavier et enregistrer les touches pressées dans un tableau que j'appellerai DATE contenant 12 cases (4 pour l'année, 2 pour le mois, 2 pour le jour, 2 pour l'heure et 2 pour les minutes), chaque appui de touches rempli une case.
Ensuite je recupere ces valeurs, je les modifie et les associe à mes variables globales.
Par contre la lecture du clavier renvoie des caractères ASCII que je devrais passer en décimal pour mettre à jour mes variables globales et pouvoir les utiliser dans mes fonctions dispayDate().

Je bûche là-dessus et je ferai un retour.

Me revoilà, j'ai gratté un bout de code sans l'avoir testé pour le moment.
Dites-moi si ça vous paraît correct.
On admet que j'ai effectué ces modifications dans mon sketch de base:

-Paramètres propres à la librairie OnewireKeypad:

#include <OnewireKeypad.h>

char TOUCHES[]={
'1','2','3',
'4','5','6',
'7','8','9',
'*','0','#'
};

OnewireKeypad <Print, 12> Clavier(Serial, TOUCHES, 4, 3, A0, 4700, 1000);




Dans le void SETUP:


Serial.begin(115200);
Clavier.addEventKey(saisieDate, ''); //accès à la sous-fonction saisieDate via la touche ''




Dans le LOOP:


Clavier.ListenforEventKey(); //contrôle si la touche '*' est pressée

-Création des variables globales:

// Configuration DateTime dest et last

byte d_annee = 55;
byte d_mois = 11;
byte d_jour = 5;
byte d_heure = 22;
byte d_minute = 4;
byte d_siecle = 19;
byte l_annee = 85;
byte l_mois = 10;
byte l_jour = 26;
byte l_heure = 1;
byte l_minute = 24;
byte l_siecle = 19;

DateTime dest(d_annee, d_mois, d_jour, d_heure, d_minute, 100+ d_siecle), // Nov 5, 1955 10:04 PM
        last(l_annee, l_mois, l_jour, l_heure, l_minute, 100+ l_siecle); // Oct 26, 1985 1:24 AM

Voici donc la Sous-fonction saisieDate():

void saisieDate(){
 byte nb_appui_touches = 0; //Compteur d'appui touches
 byte DATE[12]; //Tableau de stockage des touches appuyées en Décimal 
 for(byte i=0; i<12; i++) DATE[i]=0; //effacement données tableau
 char touche_pressee = NO_KEY; 

//Gestion appui touche clavier
 while(nb_appui_touches<12){ //Tant qu'il n'y a pas eu 11 appuis de touche
  touche_pressee = clavier.Getkey(); //Lecture touche appuyée 
  if(touche_pressee != NO_KEY && '0'<= touche_presse && touche_pressee<= '9'){ //Si une touche est pressée et qu'elle est comprise entre 0 et 9
   DATE[nb_appui_touches] = touche_pressee - 48; //Convertit le caractère ASCII en Décimal et le stocke dans le tableau
   nb_appui_touches ++;
   touche_pressee = NO_KEY;
  } //Fin if
 } //Fin while

//Mise à Jour des variables globales
 d_mois = DATE[0]*10+DATE[1]*1;//ex: DATE[0]=1 et DATE[1]=2, d_mois= 1*10+2*1=12, soit Décembre
 d_jour = DATE[2]*10+DATE[3]*1;
 d_siecle = DATE[4]*10+DATE[5]*1;
 d_annee = DATE[6]*10+DATE[7]*1;
 d_heure = DATE[8]*10+DATE[9]*1;
 d_minute = DATE[10]*10+DATE[11]*1;

} //Fin fonction saisieDate()

Donc si ça fonctionne, mes variables globales sont mises à jour pour être utilisées dans la sous-fonction displayDate(0, dest) qui suit dans le LOOP.
Je tenterai un essai d'ici ce WE.

Désolé mais ce principe de fonctionnement du programme n'est pas super.
Là, tu restes dans ton while tant que tout n'est pas saisi.
Ce qui veut dire, par exemple, que les chiffres n’apparaîtront pas sur l'afficheur au fur et à mesure de la saisie, mais tout d'un bloc à la fin.
La saisie des 12 chiffres à l'aveugle n'est pas très confortable.

Ce que je te proposais, c'était de gérer les deux en parallèle.
Gardons la fonction loop() telle que je l'ai indiquée précédemment.

Prenons comme convention que la saisie d'un caractère '*' réinitialise la saisie.

char saisie[12];
byte nbcar = 0;

void gererClavier()
{
  touche_pressee = clavier.Getkey();
  if (touche_pressee != NO_KEY)
  {
    if (touche_presse == '*')
    {
      nbcar = 0;
    }
    else if (nbcar < 12 && '0' <= touche_pressee && touche_pressee <= '9')
    {
      saisie[nbcar++] = touche_pressee;
    }
  }
}

Cette fonction n'est pas bloquante.
Sa durée d'exécution est très courte.
Elle est appelée très souvent par le loop().
La plupart du temps, elle ne fait rien.
Lorsqu'une touche est enfoncée, celle-ci est mémorisée dans le tableau "saisie".

Dans le loop(), en alternance avec cette fonction, il y a un appel à gererAfficheurs().

Cette fonction gererAfficheurs() est elle aussi appelée très souvent.
Sa première tâche sera d'analyser le contenu du tableau "saisie"
Par exemple en version longue (il y aurait plus court en utilisant des tableaux et des boucles, mais c'est moins parlant) :

void gererAfficheurs()
{
  byte siecle, annee, mois, jour, heure, minute;
  bool saisie_siecle = (nbcar >  0);
  bool saisie_annee  = (nbcar >  2);
  bool saisie_mois   = (nbcar >  4);
  bool saisie_jour   = (nbcar >  6);
  bool saisie_heure  = (nbcar >  8);
  bool saisie_minute = (nbcar > 10);

  if (nbcar >  0) siecle = saisie[ 0] - '0';
  if (nbcar >  1) siecle = saisie[ 1] - '0' + 10 * siecle;
  if (nbcar >  2) annee  = saisie[ 2] - '0';
  if (nbcar >  3) annee  = saisie[ 3] - '0' + 10 * annee;
  if (nbcar >  4) mois   = saisie[ 4] - '0';
  if (nbcar >  5) mois   = saisie[ 5] - '0' + 10 * mois;
  if (nbcar >  6) jour   = saisie[ 6] - '0';
  if (nbcar >  7) jour   = saisie[ 7] - '0' + 10 * jour;
  if (nbcar >  8) heure  = saisie[ 8] - '0';
  if (nbcar >  9) heure  = saisie[ 9] - '0' + 10 * heure;
  if (nbcar > 10) minute = saisie[10] - '0';
  if (nbcar > 11) minute = saisie[11] - '0' + 10 * minute;

  if (saisie_siecle)
  {
    //afficher la valeur de la variable siecle
    ...
  }
  else
  {
    //effacer l'affichage des deux chiffres du siècle
    ...
  }

  if (saisie_mois)
  {
    //afficher la valeur de la variable mois
    ...
  }
  else
  {
    //effacer l'affichage des deux chiffres du mois
    ...
  }

  etc.

}

bricoleau:
Désolé mais ce principe de fonctionnement du programme n'est pas super.
Là, tu restes dans ton while tant que tout n'est pas saisi.
Ce qui veut dire, par exemple, que les chiffres n’apparaîtront pas sur l'afficheur au fur et à mesure de la saisie, mais tout d'un bloc à la fin.
...

C'est bien ça que je recherche.
Je note quand même ton code pour l'étudier.
Par contre j'ai oublié une chose dans le programme, c'est de valider la saisie par la touche '#'.
L'idée:
Tant que cette touche n'est pas pressée apres entrée de la date complète, il n'y a pas de MaJ des variables globales. Une fois l'appui sur '#' fait, les variables sont mise à jour, on efface les afficheurs pendant 0.5s puis on affiche les nouvelles données.
Je vais voir si je rajoute ça dans saisieDate() ou si je crée une sous-fonction spécifique.

Encore merci pour l'aide.

Des variables globales du style siècle+annee+...+minute ne conviennent pas pour caractériser l'état du système à un instant t.

Imagine la scène : je veux saisir l'année 1955.
J'appuie sur les touches 1 puis 9 puis 5 et là stop ! On fait un arrêt sur image.
A ce moment précis, que souhaites-tu voir restitué sur les afficheurs ?

Quelle que soit la réponse, je doute que tu puisses obtenir le résultat souhaité à partir des valeurs de telles variables globales, car il manque des informations.
Alors qu'avec les variables globales ci-dessous, tu disposes de toutes les informations nécessaires à un affichage dynamique au fil de la saisie.

char saisie[12];
byte nbcar = 0; //nb de caractères saisis

Est-ce que tu vois bien la différence?

Oui je vois bien la différence.
En fait j'aurai voulu garder la "syntaxe" de la fonction displayDate qui gère les 3 rangees d'afficheurs dont une affiche l'heure réelle via un RTC.
Si je prends ta méthode, je ne garde les fonctions displayDate que pour les rangées pilotées via RTC et Last, et j'utilise ton code pour piloter la rangée d'afficheurs Destination.
Ai-je bien compris?

Bon, j'ai quelques soucis avec ma fonction saisieDate(), je ne sais pas si le delay(500) dans le LOOP qui interfère, mais je dois appuyer plusieurs fois sur '*' pour qu'il soit pris en compte (j'ai ajouté des Serial.print dans mon code pour visualiser ce qui se passe). Ensuite la boucle While() ne fonctionne pas comme prévue....
Je vais etudier le code de Bricoleau de plus près et l'adapter à mon code de base.

Je reviens dès que j'ai du nouveau.

Salut,
j'ai fait un programme avec les données de Bricoleau.
Le hic:
lorsque je veux visualiser les caractères saisis sur le Moniteur Série, déjà ça défile en continu et ça ne m'affiche que le 1er chiffre tapé...
Genre si je tape sur '1', ça m'affiche en boucle:
"Mois:11
Jour:11
Siecle:11
Annee:11
Heure:11
Minutes:11
Mois:11
Jour:11
Siecle:11
Annee:11
Heure:11
Minutes:11
....."

Voici le code:

#include<Wire.h>
#include<OnewireKeypad.h>

//--Variables Globales--
char TOUCHES[]={
  '1','2','3',
  '4','5','6',
  '7','8','9',
  '*','0','#'
};
OnewireKeypad<Print, 12> clavier(Serial, TOUCHES, 4, 3, A0, 4700, 1000);

char saisie[12]; //tableau de stockage des caracteres saisis
byte nbcar = 0; //nbre de caracteres saisis

void setup() {
  Serial.begin(115200);

}

void loop() {
  gererClavier(); //Fonction de gestion de clavier
  gererAfficheurs(); //Fonction de gestion des afficheurs
}

//------------------------Gestion Clavier-----------------
void gererClavier()
{
char touche_pressee = clavier.Getkey(); //Lecture de la touche appuyée
  if (touche_pressee != NO_KEY){ //Si une touche a été frappée -- gestion de la touche appuyée
    if (touche_pressee == '*'){ //Si appui sur '*'
      nbcar = 0; //Réinitialisation saisie en revenant au début du tableau saisie
    }
    else if (nbcar < 12 && '0' <= touche_pressee && touche_pressee <= '9'){ //Autrement, si il y a moins de 12 frappes de touches et qu'il s'agit des touches '0' à '9'
      saisie[nbcar++] = touche_pressee; //On enregistre le caractère dans le tableau saisie à la position nbcar, et on incrémente nbcar
    }
  }
}//Fin Clavier

//------------------------Gestion afficheurs---------------
void gererAfficheurs()
{
  byte mois, jour, siecle, annee, heure, minute; //creation variables locales
  bool saisie_mois   = (nbcar >  0); //Création variable qui est TRUE si au moins une touche a été frappée
  bool saisie_jour   = (nbcar >  2); // TRUE si pls de 2 touches frappées
  bool saisie_siecle = (nbcar >  4); //etc...
  bool saisie_annee  = (nbcar >  6);
  bool saisie_heure  = (nbcar >  8);
  bool saisie_minute = (nbcar > 10);

  if (nbcar >  0) mois   = saisie[ 0] - '0'; // Si au moins une touche frappée, la première valeur du tableau est le premier chiffre du mois
  if (nbcar >  1) mois   = saisie[ 1] - '0' + 10 * mois; //etc...
  if (nbcar >  2) jour   = saisie[ 2] - '0';
  if (nbcar >  3) jour   = saisie[ 3] - '0' + 10 * jour;
  if (nbcar >  4) siecle = saisie[ 4] - '0';
  if (nbcar >  5) siecle = saisie[ 5] - '0' + 10 * siecle;
  if (nbcar >  6) annee  = saisie[ 6] - '0';
  if (nbcar >  7) annee  = saisie[ 7] - '0' + 10 * annee;
  if (nbcar >  8) heure  = saisie[ 8] - '0';
  if (nbcar >  9) heure  = saisie[ 9] - '0' + 10 * heure;
  if (nbcar > 10) minute = saisie[10] - '0';
  if (nbcar > 11) minute = saisie[11] - '0' + 10 * minute;

  if (saisie_mois){
    Serial.print("Mois:"); Serial.println(mois);
  }

  if (saisie_jour){
    Serial.print("Jour:"); Serial.println(jour);
  }

  if (saisie_siecle){
    Serial.print("Siecle:"); Serial.println(siecle);
  }

  if (saisie_annee){
    Serial.print("Annee:"); Serial.println(annee);
  }

  if (saisie_heure){
    Serial.print("Heure:"); Serial.println(heure);
  }

  if (saisie_minute){
    Serial.print("Minutes:"); Serial.println(minute);
  }

} //Fin Afficheurs

Ben oui

Les deux fonctions sont exécutées en permanence et à haute fréquence, même quand il n'y a rien à faire.
C'est tout l'intérêt : cela donne l'impression de gérer le clavier et l'affichage en parallèle.

La fonction gererAfficheurs() ci-dessus conviendra bien pour actualiser des afficheurs 7 segments. Pas grave si la commande est répétée.
Par contre pour aller vers du Serial.print() il faut coder différemment pour ne pas repéter la transmission d'info.

Tu peux insérer les 3 lignes ci-dessous au début de la fonction gererAfficheurs()

void gererAfficheurs()
{
  static byte sauve_nbcar = 0;
  if (sauve_nbcar == nbcar) return; //ne rien faire si pas de nouvelle saisie
  sauve_nbcar = nbcar;

  byte mois, jour, siecle, annee, heure, minute; //creation variables locales
  bool saisie_mois   = (nbcar >  0); //Création variable qui est TRUE si au moins une touche a été frappée
  ...

Ok, merci. Je testerai ça.
Du coup il faut que je regarde ce que signifie "static". C'est cool j'apprends des choses. Merci Bricoleau.

void ma_fontion()
{
  byte titi = 0;
  static byte toto = 0;

  titi++;
  toto++;
  Serial.print("titi=");Serial.println(titi);
  Serial.print("toto=");Serial.println(toto);
}

titi est une variable locale, allouée (dans la pile) quand on entre dans la fonction, et libérée quand on sort de la fonction.
Elle est initialisée à zéro à chaque appel de la fonction.
Donc on aura toujours "titi=1" dans le terminal Serie.

toto est allouée de manière statique, c'est-à-dire qu'un emplacement ram lui est réservé en permanence, même lorsqu'on n'est plus à l'intérieur de la fonction.
C'est comme une variable globale, sauf qu'elle n'est utilisable qu'à l'intérieur de la fonction.
Elle est initialisée à zéro au lancement du programme.
Au fil des appels, on aura donc sur le terminal Serie "toto=1", puis "toto=2", etc.

Il est toujours plus propre de minimiser le nombre de variables globales.
=> toujours utiliser static au lieu d'une variable globale utilisée dans une seule fonction.

Bonjour à tous, désolé pour ce temps mort, priorités familiales et professionnelles avant tout. :wink:

Je pense laisser tomber l'approche de la gestion clavier via la librairie OneWireKeypad.

J'ai investi dans un afficheur LCD afin de tester le programme du site Mon-Club-Elec:
http://www.mon-club-elec.fr/pmwiki_mon_club_elec/pmwiki.php?n=MAIN.ArduinoExpertLCDClavierSaisieInt

Donc 1er test:
Je cable le clavier en "numérique", comme le programme le suggère.
Test ok. Normal me direz-vous.

2e test:
Je cable le clavier en "analogique", et je modifie le programme en fonction.
Résultat: c'est pas super fiable.
Premièrement, quand j'appuie sur 1, le LCD affiche 4 - superposés sur un emplacement de l'afficheur.
Deuxièmement, pour les touches 0 et #, leurs valeurs en sortie du pont diviseur de tension sont proches, donc un # est de temps en temps reconnu comme un 0...

Je vais donc revoir ma copie en procédant de la sorte:
Soit:
Câbler le clavier en "numérique" sur un Pro-mini qui lui sera dédié, et raccordé à la carte principale par liaison Série ( utilisation de la librairie EasyTransfer/SoftEasyTransfert, que j'ai déjà utilisé sur un autre projet).
Soit:
Passer sur une carte Mega qui règlera mes souci d'entrées/sorties limitées sur l'Uno et Pro-Mini.
Après cela, je pourrai me concentrer sur la partie Gestion Affichage.

Tu as interet a bien faire ton code, sinon ca va provoquer la rupture du continium espace-temps et la destruction de l' univers, ou au mieux une partie :slight_smile:

CelticLord:
Tu as interet a bien faire ton code, sinon ca va provoquer la rupture du continium espace-temps et la destruction de l' univers, ou au mieux une partie :slight_smile:

:smiley:

Bonjour à tous, j'ai bien avancé sur mon projet.
Mais je me heurte à un problème en lien avec la bibliothèque RTCLib:
Comme celle-ci n'est pas compatible pré-an 2000, lorsque je simule un "voyage dans le temps" au siècles précédents, il m'est impossible d'avoir le bon siècle (19xx et antérieur) sur le RTC.
Avez-vous une feinte pour contourner le problème?
Je pensais rajouter quelques lignes dans le fichier .h du style:
-Rajouter un argument uint8_t century dans le constructeur DateTime,
et remplacer la ligne:
uint16_t year() const {return 2000 + yOff;}
par:
uint_16t year() const {return century*100 + yOff;}
Par contre je ne sais pas quoi modifier dans le fichier .cpp

Merci pour votre aide.

Me revoilà après quelques recherches sur mon problème de date pré-an 2000.
Pourquoi ne pas utiliser la RAM du RTC pour y stocker la 2eme dizaine de l'année?
Il suffirait d'extraire cette donnée lors de l'appel de la fonction rtc.adjust(), de la stocker dans la RAM et de la lire à chaque appel de la fonction rtc.now().

Je teste ça demain et je reviens vous dire si j'ai tout faux.