Mon prog au moment de l'ultime compil, ne dépasse plus le premier mot!!?

Bonjour,
Après des jours de programmation et de test, j'arrive enfin a une version qui me permettra de mettre mon tracker solaire en place.
Dernière petite retouche, je remplace une variable pour que le retour en position prêt pour l' Aube se fasse d'un trait jusqu'au fin de course.
A la compil, le programme ne dépasse plus le premier mot de la version . Je précise, juste avant j'allume le port serie, et je tente d'imprimer Tracker V0.1... et j'ai juste Tracker et un ?
En annulant la modif, l'erreur perdure...Ahhhhhh, j'ai pas de copie avant de toucher à tout cela...
Mais bon je doit avoir un gros soucis avec la mémoire qui pourtant avec la mega est utilisée a moins de 10%

Ci joint les fichiers du projets. Merci à mes sauveurs...

CarteRelais.cpp (4.86 KB)

CarteRelais.h (417 Bytes)

IndicateControl.cpp (304 Bytes)

IndicateControl.h (790 Bytes)

ParamMoteur.h (570 Bytes)

Photo3.cpp (4.1 KB)

Photo3.h (516 Bytes)

Tracker_trashV2.ino (22.9 KB)

J'ai reussi à recréer une version qui fonctionne.
La modif ne concerne absolument pas ce qui est en cause.
La modif est à la ligne 321 du fichier principal
avant modif on a

byte ManoeuvrerSortie (byte Num,byte Sens, byte Nb) { //...//Ligne 321 if (Temps > TempsMax){OK=false;}

Et le code qui fait planter et ceci

byte ManoeuvrerSortie (byte Num,byte Sens,bool ControleTemps) { //...//Ligne 321 if ((Temps > TempsMax)&&(ControleTemps)){OK=false;}

Bien sur les appel de la procédure sont modifié en remplacant le dernier argument qui servait à rien et était à 3 par des trues et 1 false...

Je pense que c'est important de trouver ce qui merde, j'ai pas de string, et juste deux char*, on utilise que 4% de la mem de la MEGA ...:
donc c'est assez simple et je comprends vraiment pas ou est ce que je pêche.
Merci pour votre intervention cela me fera vraiment progresser.

Tracker_trashV0.8.ino (22.9 KB)

j'ai lu un peu en diagonal le code, quelques commentaires:

c'est quoi dans CarteRelais cette méthode qui s'appelle underscore?void _(byte Num,byte Sens, byte Etat);tout ce qui commence par un ou deux underscore est réservé pour le compilateur. il se peut qu'un bug subtil se cache là. Je vous suggère de mettre un nom parlant à la place.


cette méthode n'est pas propre

char* CarteRelais::Nom(byte Num) {
  //ROTA,AZIM,POMP,LIBR1,LIBR2"
  switch (Num) {
    case 0: return "ROTA\0"; break;
    case 1: return "AZIM\0"; break;
    case 2: return "POMP\0"; break;
  }
}

vous retournez un const char*, pas un char* et si Num n'est pas 0,1 ou 2 vous ne retournez rien alors que vous aviez promis au compilateur de retourner un pointeur. le \0 n'est pas nécessaire puisque une cString déclarée entre guillemets rajoute automatiquement un null à la fin.

Ce serait mieux de l'écrire comme cela

const char* CarteRelais::Nom(byte Num) {
  //ROTA,AZIM,POMP,LIBR1,LIBR2"
  static const char * lesNoms[] = {"ROTA", "AZIM", "POMP"};
  const uint8_t nbNoms = sizeof(lesNoms) / sizeof(lesNoms[0]);
  if (Num < nbNoms) return lesNoms[Num];
  return NULL; // on retourne un pointeur NULL si Num n'est pas valide.
}

il faut bien sûr modifier le .h en conséquence    const char* Nom(byte Num);


dans CarteRelais.cpp vous faites

  for (byte i = 0; i < 8; i++) {
    Serial.print(F("\nxxxxxxxxxxxx"));
    Serial.print(PINS[i]);
  }

donc une itération de 0 à 7 soit 8 éléments, hors le tableau PINS a été déclaré comme ayant 7 élémentsconst int PINS[7]        PROGMEM  = {22, 23, 24, 25, 26, 27, 28}; ==> il faut changer le 8 et 7

  for (byte i = 0; i < 7; i++) {
    Serial.print(F("\nxxxxxxxxxxxx"));
    Serial.print(PINS[i]);
  }

dans la version Tracker_trashV0.8

Dans Tracker_trashV0.8.ino vous déclarez

  int TempsMini;
  int TempsMax;

puis avez un unsigned long Temps; ça vous fait faire des comparaison entre entiers signés et non signés. déclarez tout vos temps en unsigned long, ce sera plus propre.

  unsigned long TempsMini;
  unsigned long TempsMax;
  unsigned long Temps;

toujours dans ce .ino vous avez les variables globales suivantes

static int Code;
static Photo3 Photo[2];
static CarteRelais Sortie;
static ParamMoteur PM[2];
static IndicateControl Axe[2];

==> il est inutile de les déclarer static, elles le sont par définition puisque globales. utilisez simplement

int Code;
Photo3 Photo[2];
CarteRelais Sortie;
ParamMoteur PM[2];
IndicateControl Axe[2];
bool PanneauBas;
bool FDCourse;
bool OK;

(notez que Code n'est jamais utilisé, vous pouvez le virer)

toujours dans les déclarations ça ne sert absolument à rien de mettre vos octets constants en PROGMEM.

//IO Pins
const byte ROTA      PROGMEM = 0;//MOTEUR 1
const byte AZIM      PROGMEM = 1;//MOTEUR 2
const byte POMP      PROGMEM = 2;//MOTEUR 3

//Carte relais 22à28
const byte PIN_INTER PROGMEM = 29;//LE PIN INTER
const byte RETRO     PROGMEM = 7;//Rétroéclairage


// Fins de courses
const byte F_ROTA    PROGMEM = ROTA + 30;//voie 04 Carte
const byte F_AZIM    PROGMEM = AZIM + 30;//voie 05 Carte
const byte F_POMP    PROGMEM = POMP + 30;//voie 08 Carte

définissez les juste comme const byte, le compilateur va tout optimiser en remplaçant directement la valeur dans le code puisque ça tient sur un octet.


vous appelez ManoeuvrerSortie() avec -1 comme paramètreManoeuvrerSortie (ROTA, -1, 3);/code]et la signature de la fonction est [code]byte ManoeuvrerSortie (byte Num, byte Sens, byte Nb)ce second paramètre est donc non signé. Vous le traitez sans doute correctement puisque je vois des comparaison avec 255, ce qui est effectivement la valeur convertie de -1 mais ce n'est pas propre.
Vous devriez définir un enum en début de CarteRelais.h (en dehors de la classe)enum : byte {SENS_POSITIF = 1, SENS_NEGATIF = 255}; et replacez dans les appels le 1 par SENS_POSITIF et le )1 par SENS_NEGATIF, par exempleManoeuvrerSortie (ROTA, -1, 3);devientManoeuvrerSortie (ROTA, SENS_NEGATIF, 3);et la fonction MotSens devient

char* MotSens(byte Sens) {

  static char buff[5];

  switch (Sens) {
    case SENS_POSITIF:    strcpy( buff, "[ [>");
      //DEBUGS_PRINT(buff);
      break;
    case SENS_NEGATIF:  strcpy( buff, "[ <[");
      //DEBUGS_PRINT(buff);
      break;
    default:   //DEBUGS_PRINT(F("\nERR Sens"));DEBUGS_PRINT(Sens);
      strcpy( buff, "ERR");
      break;
  }

  return buff;

}

(notez que vous êtes super court pour la longueur du buffer - ne pas mettre plus de 4 caractères.)

dans la classe la méthode RelaisAnnexes() devient alors

void CarteRelais::RelaisAnnexes(byte Sens, bool Etat) {
  if (Etat) { //Pour lancer
    switch (Sens) {
      case SENS_POSITIF:   Relais(PINS[R_INVERS], LOW); break;
      case SENS_NEGATIF: Relais(PINS[R_INVERS], HIGH); break;
    }
    Relais(PINS[R_MASSE], HIGH);
  }
  else { //Pour Finir
    Relais(PINS[R_MASSE], LOW);
    Relais(PINS[R_INVERS], LOW);
  }
}

bref - changez partout les 1 et -1 et 255 pour utiliser un truc uniforme.

dans cette version le 3ème paramètre de ManoeuvrerSortie() - byte Nb - est inutilisé.

toujours dans cette fonction, la variable locale int Contact;//Valeur Contactique n'a pas de valeur par défaut et si vous ne passe pas dans le if (TempsMax > 300) {elle sera aléatoire quand vous ferez le test final

  if (Contact)                              {
    DEBUGS_PRINT(F("\n=>>>>>>0:Fin de Course!!"));
    return 0;
  }
  else {
    if (ErreurMini < abs(Photo[1].Erreur[Num]))  {
      DEBUGS_PRINT(F("\n=>>>>>>2:Amélioré"));
      return 2;
    }
    else                                  {
      DEBUGS_PRINT(F("\n=>>>>>>1:Pas  Mieux"));
      return 1;
    };

(notez que le dernier point virgule n'est pas nécessaire)

==> initialisez Contact à 0 par exemple si ça fait du sens  int Contact = 0;//Valeur Contactique


=> appliquez les même changements dans la version avec Tracker_trashV2.ino


votre façon de gérer l'EEPROM octet par octet n'est pas la plus efficace utilisez put() et get() et mettez toutes les données dans une structure, ce sera bien plus simple à gérer.

J-M-L,
Waouh, pour de la diagonale rapide c'est très pointu!
Merci pour le temps que vous avez passez à éplucher tout cela; Cela va me faire beaucoup progresser!
Là je part poser le tracker malgré l'instabilité potentielle, et la canicule: il me faut pomper de l'eau au maximum pour mes nombreux arbres.
Puis ce soir je regardes cela au frais.
Vraiment très gros Merci J-M-L! ,avec ceci j’espère l'affaire classée 8)!

à noter que le tableau trop court que pointe JML est probablement la cause du fonctionnement anormal remarqué. J'ai déjà eu le cas pour la même raison, un tableau trop court, ça fonctionnait par chance, et autrechose qui n'avait absolument rien à voir partait en vrille à la moindre modif minime

@J-M-L: J'ai commencé a utiliser "" en première position pour nommer une variable a cause du problème à nommer "SP" pour "Set Point" pendant du "PV" pour "Point Value" couramment utilisé en automatisme et plus particulièrement en régulation.
Problème évoqué ici Why I can not use "SP" as parameter in my function? - Programming Questions - Arduino Forum
Le "
" pour appeler la sous variable principale est bien pratique et permet une bonne lisibilité genre XXX..SP, XXX..PV ou si plusieurs variables XXX.[99].SP.
Bon a proscrire alors d'après J-M-L car réservé au compilateur en première position... J'utiliserait desormais le "V" en lieu et place du "
" pour éviter les problèmes d'instabilité mémoire d'origine inconnue.
Si vous tombez sur ce post, testez cette hypothèse.

bricofoy:
à noter que le tableau trop court que pointe JML est probablement la cause du fonctionnement anormal remarqué. J'ai déjà eu le cas pour la même raison, un tableau trop court, ça fonctionnait par chance, et autrechose qui n'avait absolument rien à voir partait en vrille à la moindre modif minime

@fricofoy , vous faites référence au char * qui est défini pile poil pour le nombre de caractères utilisé?

J-M-L:
Code: [Select]
char* MotSens(byte Sens) {

static char buff[5];

switch (Sens) {
case SENS_POSITIF: strcpy( buff, "[ [>");
//DEBUGS_PRINT(buff);
break;
case SENS_NEGATIF: strcpy( buff, "[ <[");
//DEBUGS_PRINT(buff);
break;
default: //DEBUGS_PRINT(F("\nERR Sens"));DEBUGS_PRINT(Sens);
strcpy( buff, "ERR");
break;
}

return buff;

}
(notez que vous êtes super court pour la longueur du buffer - ne pas mettre plus de 4 caractères.)

C'est bien ce problème que vous avez eu? Si c'est le cas je trouve ça un peu piege à c...

En tout cas merci pour votre regard et partage d'expérience!

Oui le underscore dans l’espace global des noms est réservé et vous pouvez tomber aussi sur certaines macros effectivement SP etc

Je préfère le camelCase pour nommer mes variables

Ca remarche ?

(Je pense que bricofoy parlait du tableau des PINS)

J-M-L:
(Je pense que bricofoy parlait du tableau des PINS)

oui

@Brolensky : avec un tableau trop court, le problème c'est que quand il est employé, au moment de lire/ecrire dans la dernière case, puisqu'il est trop court on se retrouve à écrire dans la ram à la place d'autre chose, qui peut être une autre variable, un pointeur, etc... et forcément puisqu'on se retrouve à cet endroit avec une valeur du coup différente de ce qu'elle devrait, bah ça donne une résultat totalement inattendu à l'execution, soit un blocage du programme, soit un comportement complètement aléatoire, soit des variables qui changent de valeurs toutes seules, etc etc

bricofoy:
le problème c'est que quand il est employé, au moment de lire/ecrire dans la dernière case, puisqu'il est trop court

c'est réellement un souci qu'en écriture, pas en lecture. On va juste lire n'importe quoi en mémoire.

c'est exactement ce qu'a (déjà) écrit bricofoy :

bricofoy:
puisqu'il est trop court on se retrouve à écrire dans la ram à la place d'autre chose

je réagissais sur cette partie de la phrase

problème c'est que quand il est employé, au moment de lire/ecrire dans la dernière case

par ailleurs il s'agit bien d'une lecture dans le code de @Brolensky

for (byte i = 0; i < 8; i++) {
    Serial.print(F("\nxxxxxxxxxxxx"));
    Serial.print(PINS[i]);
  }

Salut les gars,
Bon j'ai fait:

  • toutes les modifs suggérées par J-M-L,
  • celles que j'avais cru comprendre d'après la remarque de bricofoy,
  • viré tous le "_".

Et ca merde encore au premier mot de l'affichage de la version lorsque j'essaye d'ajouter la variable de bypass du contrôle de delais dans la procédure ManoeuvreSortie (Ce qui avait révélé le problème de mémoire.)

Pour ce qui est du point réellement soulevé par bricofoy, j'ai pas pu tester: j'étais à l'arrosage ce matin.
Ceci dit @5_cylindre (merci pour ton regard) je suis conscient des mécanismes générant l’instabilité mémoire, et je rejoins J-M-L quand à l’innocuité supposé d'une lecture erronée.
Ceci dit ca merde, même avec une fin de boucle de lecture fixée à 7 au lieu du 8 (A la réflexion, la modif fessait partie du pool J-M-L et a donc était injectée).

Ce programme avec pas mal d'option inutilisées est une transposition rapide de mon programme de gestion climatique de la serre (3 capteurs temp/hygro, 3 portes avec codeur, brumisation, SMS, Sortie serveur, + language de com (L/E) ouvert multi-paramètres sur 5 digits via SMS ou Serial...(bref la méga est raz la gueule)).
Ca marchait jusqu'au jour ou l'ouvrant a fait chanter les crémaillères de l'ouvrant supérieur toute la nuit.
J'ai cherché le bug mais le programme est trop lourd et ca fait 2 ans que je laisse tout ouvert, mais bon une vrai gestion climatique serait un gros plus pour le contrôle de croissance et de maladie...

Bref la, c'est plus simple juste 5% de la capa Mega, mais ca merde alors que je suis plus tout seul (au passage re-merci): c'est pire que les rats des champs (détail important) dans une serre ces instabilité mémoires!!!

Vous avez des idées pour localiser et capturer le rongeur?

Vous avez des idées pour localiser et capturer le rongeur?

vous pouvez poster le code modifié qui bug? (un zip s'il y a plusieurs fichiers)

J-M-L:
vous pouvez poster le code modifié qui bug? (un zip s'il y a plusieurs fichiers)

Voici le code "en ZIP", la modif SENS_POS et SENS_NEG comporte quelques inversions et quelques manques , mais c'est le nouveau qui plante le 1.1 avec vos modifs.

Avec quel logiciel vous êtes IDE Arduino? Ou autre?

Tracker_trashV1.1.zip (11.9 KB)

Et la j'arrive m^me plus à compiler avec ma 1.2 ou je corrige le bug des SENS et clarifie le OK en remplaçant par un bool Continuer (pour une boucle while!)

Message compilateur:
unable to create a folder to save the sketch: mkdir sketch: Accès refusé.
Erreur de compilation pour la carte Arduino Mega or Mega 2560

Bibliothèque non valide trouvée dans D:\Arduino\libraries\libraries\PID_Auto_Tune : aucun fichier d'en-tête (.h) trouvé dans D:\Arduino\libraries\libraries\PID_Auto_Tune

Tracker_trashV1.2.ino (23.2 KB)

Alors que le dossier existe et semble bien plein...

CarteRelais.cpp (5.25 KB)

CarteRelais.h (467 Bytes)

IndicateControl.cpp (304 Bytes)

IndicateControl.h (795 Bytes)

ParamMoteur.h (570 Bytes)

Photo3.cpp (4.66 KB)

Photo3.h (592 Bytes)

Tracker_trashV1.2.ino (23.2 KB)

il doit essayer de créer un répertoire temporaire pour tout copier dedans et ensuite compiler. reste-t-il de la place sur ce disque ? avez vous les bons droits ? je suis sur Mac ou Linux, si c'est un souci windows ce n'est pas mon fort...

vu le message sur la bibliothèque vous devez avoir un souci d'installation... moi je referais une installation propre de l'IDE et des bibliothèques (même si c'est un peu fastidieux).

attention dans le code pour les swicth c'est default en minuscule, pas Default

void  ManoeuvreManu() {
  //Pas de controle de delais, mais si plus de direction exit de la manoeuvre
  switch (Photo[0].Direction) {
    case D_UP   : ManoeuvrerSortie (AZIM, SENS_POS, false); break;
    case D_DOWN : ManoeuvrerSortie (AZIM, SENS_NEG, false); break;
    case D_EST  : ManoeuvrerSortie (ROTA, SENS_POS, false); break;
    case D_OUEST: ManoeuvrerSortie (ROTA, SENS_NEG, false); break;
    default: break; // ATTENTION pas Default
  }
}

dans Photo3 cette méthode ne retourne rien si l'index n'est pas un des 3

byte Photo3::Borne(byte Index) {
  switch  (Index) {
    case ZENITH: return P_ZENITH;
    case EST   : return P_EST;
    case OUEST : return P_OUEST;
  }
}

idéalement il faudrait définir un code d'erreur.

à part quelques warning, ça compile sans souci sur mon mac

j'ai vu 2 endroits où le code peut faire une division par 0 dans Photo3

  Photo3::Erreur[ROTA] /= this->Moyenne;
  Photo3::Erreur[AZIM] = this->V[ZENITH].PV - (this->V[EST].PV + this->V[OUEST].PV) / 2;
  Photo3::Erreur[AZIM] /= this->Moyenne;

si la moyenne est nulle. faudrait tester avant de diviser

ne testez jamais une égalité pour un temps

    if (DelaisCrepuscule == 18000000) { //5Heures
      this->Zero();
    };

vous n'êtes pas sûr de tomber sur la bonne milliseconde

    if (DelaisCrepuscule >= 18000000UL) { //5Heures
      this->Zero();
    };

(et rajoutez UL pour forcer le type unsigned long)

dans ces tests, êtes vous sûr qu'il faille un && ?

      if ((Temps > TempsMax ) && (ControleDelais))       {
        Continuer = false;
      }
      if ((Temps > TempsMini) && (Contact))              {
        Continuer = false;
      }
      if (Manu && !Photo[0].Direction)                 {
        Continuer = false; //Fin du mode Manu
      }

par exemple je me dis intuitivement qu'il ne faut pas continuer si le temps max est dépassé OU qu'on a touché un contact (si c'est ce que ces variables veulent dire)

Bonjour @J-M-L! Merci pour ce retour.

J-M-L:
il doit essayer de créer un répertoire temporaire pour tout copier dedans et ensuite compiler. reste-t-il de la place sur ce disque ? avez vous les bons droits ? je suis sur Mac ou Linux, si c'est un souci windows ce n'est pas mon fort...

vu le message sur la bibliothèque vous devez avoir un souci d'installation... moi je referais une installation propre de l'IDE et des bibliothèques (même si c'est un peu fastidieux).

Oui je me doute, mais ce qui me chagrine c'est que d'habitude il y arrive...

J-M-L:
attention dans le code pour les swicth c'est default en minuscule, pas Default

void  ManoeuvreManu() {

//Pas de controle de delais, mais si plus de direction exit de la manoeuvre
 switch (Photo[0].Direction) {
   case D_UP   : ManoeuvrerSortie (AZIM, SENS_POS, false); break;
   case D_DOWN : ManoeuvrerSortie (AZIM, SENS_NEG, false); break;
   case D_EST  : ManoeuvrerSortie (ROTA, SENS_POS, false); break;
   case D_OUEST: ManoeuvrerSortie (ROTA, SENS_NEG, false); break;
   default: break; // ATTENTION pas Default
 }
}

C'est corrigé: mais c'était passé au compil!!

J-M-L:
dans Photo3 cette méthode ne retourne rien si l'index n'est pas un des 3

byte Photo3::Borne(byte Index) {

switch  (Index) {
   case ZENITH: return P_ZENITH;
   case EST   : return P_EST;
   case OUEST : return P_OUEST;
 }
}



idéalement il faudrait définir un code d'erreur.

Ca sert a mettre a jour les menu . comme j'appelle avec les constantes normalment c'est bon. J'ai ajouté un default sans "Default".

J-M-L:
à part quelques warning, ça compile sans souci sur mon mac

Alors, chez moi aussi il compile puis il merde a l'excution. La ou ce serait interessant c'est de le balancer dans une autre MEGA et voir si le bug du "dépasse pas le premier mot" 'est reproductible (je suis sous windows et j'utilise du générique)

J-M-L:
j'ai vu 2 endroits où le code peut faire une division par 0 dans Photo3

  Photo3::Erreur[ROTA] /= this->Moyenne;

Photo3::Erreur[AZIM] = this->V[ZENITH].PV - (this->V[EST].PV + this->V[OUEST].PV) / 2;
 Photo3::Erreur[AZIM] /= this->Moyenne;



si la moyenne est nulle. faudrait tester avant de diviser

ne testez jamais une égalité pour un temps


if (DelaisCrepuscule == 18000000) { //5Heures
     this->Zero();
   };



vous n'êtes pas sûr de tomber sur la bonne milliseconde


if (DelaisCrepuscule >= 18000000UL) { //5Heures
     this->Zero();
   };



(et rajoutez UL pour forcer le type unsigned long)

Pour la div par zero quasi impossible de jour, mais de nuit...
Je comprends pas "ne testez jamais": c'est une variable? Après il ,y a l'histoire du jour ou millis repasse à zéro mais là on y est pas encore... :Merci de développer.

J-M-L:
dans ces tests, êtes vous sûr qu'il faille un && ?

      if ((Temps > TempsMax ) && (ControleDelais))       {

Continuer = false;
     }
     if ((Temps > TempsMini) && (Contact))              {
       Continuer = false;
     }
     if (Manu && !Photo[0].Direction)                 {
       Continuer = false; //Fin du mode Manu
     }



par exemple je me dis intuitivement qu'il ne faut pas continuer si le temps max est dépassé **OU** qu'on a touché un contact (si c'est ce que ces variables veulent dire)

L'annulation du temps Max concerne le cas spécifique du retour de nuit en position aube, qui sera fait d'une traitre, jusqu'au fin de course associé. Normalement pas de risque, cela dure 15 min environs, mais il serait plus sage de borner l'opération.
On annule l'effet du conctact le temps de démarrer et aller dans l'autre sens, mais a l'usage c'est pas très top!Il me faudrait ajouter 2 fin de courses (la il n'y en a qu'un par voie, pour la rota il fait les deux et )

Franchement au final rien de bien exotique qui ne puisse expliquer ce fonctionnement chaotique, non?
Pour moi, la priorité serait d'injecter sur une autre MEGA.

Je ne pense pas que votre mega soit en cause si vous pouvez faire tourner d’autres programmes.

passez en 115200 ou 230400 bauds au lieu de 9600. une vitesse lente va vous ennuyer au debug

Comme le port série est asynchrone et Votre microprocesseur quand même assez rapide, le fait que vous ne voyez que quelques caractères ne veut pas dire que ça plante juste à cet endroit. C’est juste le nombre de caractères que l’interface série avait eu le temps d’émettre avant que le programme ne plante ou se trouve dans une boucle infinie avec les interruptions désactivées. Essayez avec des affichages de débug de 1 caractère en en mettant après chaque ligne de programmation. Vous verrez mieux jusqu’où il va. Ou appelez flush() pour bloquer jusqu’à émission totale du message.

Je comprends pas "ne testez jamais": c'est une variable? Après il ,y a l'histoire du jour ou millis repasse à zéro mais là on y est pas encore... :Merci de développer.

Millis() n’est pas garanti de passer par toutes les millisecondes, il en saute parfois une de temps en temps en fonction des arrondis et du temps pris ailleurs dans le reste de votre code notamment s’il y a des interruptions comme Serial ou SPI ou I2C, d’autres timers etc...

j'ai vu ce commentaire//enum : float {PETITE_PRECISION = 0.05, SENS_NEG = 255};//TODOça ne va pas aller, un enum ne peut être qu'un nombre entier, donc 0.05 n'est pas possible.

Vous pourriez expliquer en 2 mots ce que doit faire ce programme ?