Arduino ne sait plus compter

Bonjour,

Alors perso je rigole bien, mais en vrai c'est pas drôle. J'utilses un capteur de luminosité Celui-ci , j'ai borné ses résultats entre 200 et 800 et je les convertis en 0 255 afin d'allumer une led avec la même intensité. La carte est une arduino Uno.

Afin de savoir le pourcentage de luminosité compris dans la fourchette dédiée, je fais un bete et simple produit en crois en utilisant la valeur de sortie du capteur *100 et /800. Sauf que le résultat est juste totalement abérant ! En affichant les résultats, par exemple le capteur me dis 631, le calcul vaut 0 et la valeur du capteur *100 vaut -2351.

Je veux bien dire que je suis nul en math mais là quand même ahah

Voilà le code, le calcul se trouve dans la dernière boucle tout à la fin "convert()"

const int analogInPin = A0;  // Analog input pin that the potentiometer is attached to
const int analogOutPin = 9; // Analog output pin that the LED is attached to

int sensorValue = 0;        // value read from the pot
int outputValue = 0;        // value output to the PWM (analog out)
float rawRange = 1024; // 3.3v
float logRange = 5.0;
float ValConvert=0;
int ValConvert1=0;

void setup() {
  // initialize serial communications at 9600 bps:
  analogReference(EXTERNAL); //
  Serial.begin(9600);
}

void loop() {
  // read the analog in value:
  sensorValue = analogRead(analogInPin);
  // map it to the range of the analog out:
  sensorValue = constrain(sensorValue, 200, 800);

  outputValue = map(sensorValue, 200, 800, 0, 255);
  // change the analog out value:
  analogWrite(analogOutPin, outputValue);
  
  // print the results to the serial monitor:
  //Serial.print("Sensor = " );
  //Serial.print(sensorValue);
  //Serial.print("  Lux = ");
  //Serial.print(RawToLux(sensorValue));
  //Serial.print("  Output = ");
  //Serial.print(outputValue);  
  //Serial.print("  Val convert = ");
  Serial.print(convert(sensorValue)); 
  Serial.println(" ");

  // wait 2 milliseconds before the next loop
  // for the analog-to-digital converter to settle
  // after the last reading:
  delay(1500);
}

float RawToLux(int raw)
{
  float logLux = raw * logRange / rawRange;
  return pow(10, logLux);
}

float convert(int sensorValue)
{ Serial.print("  Val sensor = ");
  Serial.print(sensorValue);
  Serial.print("  ");
  Serial.print(ValConvert1); 
  Serial.print("  ");
  ValConvert1= sensorValue * 100; 
  ValConvert=ValConvert1/800;
  return ValConvert;
}

Perso je me garderai bien de faire un titre pareil, j'aurai trop peur de me ridiculiser en accusant le matériel.
Le problème est OBLIGATOIREMENT dans l'interface chaise clavier.

T'es tu servi du tuto d'Eskimon pour apprendre la programmation sur carte Arduino ?

Pour régler ton problème ne travaille pas sur un code complet tu n'y arrivera pas.
Il faut traiter les problèmes UN par UN.

Crée des petits programmes pour mettre au point fonction par fonction.
Une fois que toutes les fonctions sont correctes assemble le tout.

Je sais très bien que le problème vient pas de la carte, c'était pour faire sourire que j'ai mis ce titre.

J'ai pas vu le tuto en question j'ai appris autrement car je maitrisais la programmation C++/C avant.

Même en mettant en place la fonction toute seule à coté, j'ai le même souci. J'ai testé en mettant une constante en paramètre de la fonction et ça fonctionne, mais dès que je mets la variable du capteur, ça part en vrille...

Bon j'ai fais le calcul en 1 seule ligne au lieu de faire *100 puis /800, je fais direct *0.125 et ça marche, franchement c'est trop bizarre. Car j'ai vu avant que en dessous de la valeur du capteur 320, le calcul *100 /800 était bon, mais au dessus, non.

Je n'ai pas regardé mais quand cela fonctionne avec certaines valeurs et pas à d'autres il y a souvent des mauvais choix de type de variable.
Le fait de ne pas décomposer en calculs élémentaires évite peut-être d'avoir à gérer des nombres non adaptés au type de variables qui doit les recevoir.

Vykel:
Bon j'ai fais le calcul en 1 seule ligne au lieu de faire *100 puis /800, je fais direct *0.125 et ça marche, franchement c'est trop bizarre.

Bonjour,

Pas du tout bizarre ...

Un compilateurs C, c'est bien plus permissif qu'un compilateur Pascal, cela accepte les transtypages implicites sans souci ..... et cela autorise les débordements de capacité (par exemple sur un int pour ce cas particulier)

Un habitué saura réveiller facilement le : "je maitrisais la programmation C++/C avant"

DSL :confused:

Serge .D

Bonjour,

C'est un problème de typage a priori.
Pour éviter ce genre de soucis, quand tu fais des calculs sur des entiers pour ressortir des flottants, c'est beaucoup plus sûr d'écrire les choses comme ça:

ValConvert = (float)ValConvert1 / 800.;

Vykel:
Bon j'ai fais le calcul en 1 seule ligne au lieu de faire *100 puis /800, je fais direct *0.125 et ça marche, franchement c'est trop bizarre. Car j'ai vu avant que en dessous de la valeur du capteur 320, le calcul *100 /800 était bon, mais au dessus, non.

Bon, peut-être une piste :

320*100 = 32000 , cela tient sur un int (16 bits dont un bit de signe, reste 15 bits de valeur) ensuite très vite en augmentant ( vers 32768), dépassement de capacité, on passe en valeurs négatives ....
Juste essayer pour voir de remplacer int par word ( non signé) pour obtenir 16 vrais bits de valeur.
Puis ensuite essayer long ...

Serge .D

Ou puisque tu maîtrise le C utiliser les types, je connais pas le nom exact (mais je sais que je ne maîtrise pas).

int8_t uint8_t
int16_t uint16_t
int32_t uint32_t

Je ne crois pas que gcc-avr accepte les entiers 64 bits mais je ne suis sûr de rien.

Edit :
Je viens de voir ceci :

ValConvert1 = sensorValue * 100 ; 

ValConvert  = ValConvert1 / 800 ;

Au lieu de multiplier par 100 puis diviser par 800 il aurait été plus astucieux de ne diviser que par 8.
ValConvert = sensorValue / 8 ;
Cerise sur le gâteau on économise une ligne de code donc de la mémoire flash qui est limitée dans un micro-contrôleur.
Un peu de réflexion avant d'écrire n'a jamais fait de mal.

Bonjour,

même problème pour moi hier...
Un calcul sur uniquement des entiers retourne un résultat entier, même stocké dans un float.

comme l'indique 3Sigma:
Pour forcer un résultat en décimal, mettre un au moins des nombres avec " . " ou ".f" ou ".o"

voilà mon test d'hier

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

void loop() {
  float nivAccusReel=0;
  
  nivAccusReel=7.52; 
  Serial.print(F("Etat Accus = "));Serial.println(nivAccusReel);   //7.52
  
  nivAccusReel=2*5*300/1024;       //=2.93
  Serial.print(F("Etat Accus = "));Serial.println(nivAccusReel);  //2.00 !!!!!!
  
  nivAccusReel=2.f*5*300/1024;       //=2.93
  Serial.print(F("Etat Accus = "));Serial.println(nivAccusReel);   //2.93 

  nivAccusReel=2*5*300/1024.;    //=2.93   
  Serial.print(F("Etat Accus = "));Serial.println(nivAccusReel);   //2.93

   nivAccusReel=2.*5.0*300.0f/1024.f;     //=2.93
  Serial.print(F("Etat Accus = "));Serial.println(nivAccusReel);    //2.93
  
  
  Serial.println();Serial.println();
  
    
  
  delay(2000);
}

Sauf que les règles de trois, ne sont valables que dans les système linéaires.. Et la réponse de ton capteur en fonction de la luminosité et tout sauf cela.

Sauf que les règles de trois, ne sont valables que dans les système linéaires.. Et la réponse de ton capteur en fonction de la luminosité et tout sauf cela.............;

Exact : (réponse log)

lumi.PNG

Beaucoup de capteurs donnent des résultats non linéaires (CTN, Photorésistances etc ...)

Alors pour un petit intervalle on peut faire comme si ...

Serge .D

Souvent on peut " linéariser par morceau " mais il faut y aller avec des pincettes > il faut maîtriser ce qu'on mesure, et là ce n'est pas gagné d'avance.

On peut aussi approximer une courbe avec un polynome de degrés maximal N-1 si N est le nombre de points, les tableurs ou mieux qucs font cela très bien.

Dans un premier il me semble plus sage de convertir la mesure en log en mesure linéaire ( bien vu jpchenal)
Ensuite poser les calculs litéraux sur une feuille de papier et rechercher les simplifications possibles.
Coder au dernier moment.

Eventuellement se rappeler que :
log(a) + log(b) = log(a*b)
log(a) - log(b) = log(a/b)

Tout d'abord merci à tous pour vos réponses.

Je viens de me repencher un peu plus sur le projet. J'ai borné mon capteur pour qu'il évolue entre 200 et 800, pour un pourcentage allant de 0 à 100% pour ces mêmes valeurs.
J'en tire donc que y=6x + 200, la valeur convertie ici est "x" donc x=(y-200)/6

float ValConvert=0;
int sensorValue = 0;

int convert(float sensorVal)
{ ValConvert= (sensorVal -200)*0.1666667; 
  return ValConvert;
}

Dans ton exemple tu vas récupérer un entier signé 16 bits, alors que le résultat de la fonction sera un nombre décimal, j'ai pas suivi mais est-ce bien cela que tu veux?

J'aurais plus écrit cela:

float convert (unsigned int value)
{
	return (float (value) - 200.0) / 6.0;
}

J'ai mis unsigned int parce que je suppose que ton capteur est analogique et que la donnée est une valeur directement retournée du registre relié au convertisseur analogique -> numérique.

Si tu veux plutôt injecter un nombre décimal dans la fonction cela donne:

float convert (float value)
{
	return (value - 200.0) / 6.0;
}

Faut pas se prendre la tête mais toujours adopter une nomenclature en programmation, que tu applique partout.
Ici pour être sûr du résultat tout est de type décimal (float ou double) dans le calcul.

Si tu écris par exemple:

x = 123;

Par défaut, 123 est un entier signé du largeur de 16 bits ! Et non pas 8 bits (ce qui aurait été suffisant ici) comme on pourrait le penser, ça c'est le compilateur qui gère...

Donc, si tu écris:

x = 123 * 1000;

Étant donné que le calcul est effectué en 16 bits, x = -8072 (il y a dépassement des 16 bits)

Du coup, pour bien faire il faut écrire:

x = 123l * 1000l;

L signifie long (32 bits signés), le calcul est donc effectué sur 32 bits, et le résultat est bien x = 123000
On peut également écrire ul (unsigned long) pour aller plus loin (32 bits non signés).

Pour un calcul décimal, il convient d'écrire:

x = 123.0 * 1000.0;

Langage C++: Les types de données

Je sais pas si on peut faire plus clair. Merci beaucoup sylvainmahe, je vois plus le problème.

Bon le truc c'est qu'après j'utilise la valeur convertit dans une liste d'entiers prédéfinis afin d'envoyer une info, donc le résultat de la convertion doit être un entier :slight_smile:

Par conséquent, j'utilise un "int" plutôt qu'un "float" bien que j'utilises des décimaux pour le calcul.

Je ne suis plus le sujet que de loin donc tu fais déjà peut-être ce que je vais recommander.

En physique on cherche toujours à conserver le maximum de précision dans les calculs intermédiaires et on évite absolument d'arrondir en cours du calcul, sinon les résultats finaux deviennent vite faux.
En particulier il faut écrire les formules complètement pour faire la chasse au multiplication puis division par le même nombre qui sont générateur d'erreurs d'arrondis facilement évitables.

Je comprends que pour certaines applications il faille des nombres entiers mais ne reprends jamais des résultats arrondis pour continuer des calculs, ce qui veut dire qu'il faut multiplier les variables.
Transformer un float vers un entier constitue un arrondi.

Vykel pas de soucis.

68tjs tu as entièrement raison, je suis d'accord avec toi, même si effectivement j'ai (par malheur) rencontré dans des forums que float = vilain méchant pas bien !! Alors qu'en réalité tout se passe bien quand on ne fait pas nimportenaouak avec, genre un incrément float x += 1.01, ce qui donnera assez rapidement des machins du genre 5.49998569...

Vykel, je ne connais pas toute ta problématique, ce que tu veux calculer etc..., mais si vraiment tu veux sortir de ta fonction un entier, alors je te conseille d'arrondir au plus proche, exemple:

unsigned int convert (float value)
{
	return Math.round ((value - 200.0) / 6.0);
}

Parce que sinon si tu laisses faire ton programme, un décimal vers un entier donne obligatoirement un arrondi non pas au plus proche, mais plutôt à l'entier inférieur (c'est peut être ce que tu souhaites).