Réalisation d'un afficheur de vitesse pour voiture

Bonjour

J'ai démarré un nouveau projet, avec comme but d'afficher la vitesse de ma voiture dans un afficheur qui puisse être mieux placé dans le champ de vision que le compteur d'origine (dans le style speed visio de Valeo).

Comme je possède un véhicule relativement récent, il y a un fil info vitesse, et j'ai pu y brancher un nouveau cable en accédant aux branchements du compteur d'origine grâce à des informations trouvées sur les forums auto.

Pour la phase suivante, j'ai espionné avec un oscillo numérique de poche les infos qui circulaient sur ce cable.

Le signal est un signal carré, le niveau bas est à 0, le niveau haut à 13 V.
A 40 km/h, la demi-période est d'environ 10 ms, à 80 km/h de 5 ms. La loi qui donne la vitesse serait donc: vitesse=400/demi-période.

Enfin, j'ai développé le prototype.

Pour la partie électronique, je récupére le signal du cable sur une entrée numérique de l'arduino, après l'avoir adaptée au niveau de tension acceptable, en utilisant un pont diviseur (6 k/10 k), ce qui donne 4,88 V en entrée en niveau haut.

Pour la partie logicielle, je me contente pour l'instant, dans la boucle principale, de déterminer la demi-période du signal, d'en déduire la vitesse, et de l'afficher.

Je n'ai pas opté, dans ce projet, pour l'utilisation des interruptions (par exemple sur signal montant), mais plutôt sur un test en continu de la valeur (haute ou basse) de mon entrée vitesse.

Voici la partie du code qui effectue le "calcul" dans la fonction principale void loop()

valBitInfoVitesse=digitalRead(bitInfoVitesse);
  
	while(valBitInfoVitesse==LOW)
  	{
    		valBitInfoVitesse=digitalRead(bitInfoVitesse);
    	}
   
  
	t=micros();  
	
	while(valBitInfoVitesse==HIGH)
  	{
    		valBitInfoVitesse=digitalRead(bitInfoVitesse);
    	}
  
  	ecartMicroS=micros()-t;
   	vitesse=400000/ecartMicroS;
    	ecrireNombre(vitesse);

J'ai testé l'ensemble sur mon véhicule, en fait en deux fois. Dans la première version, j'affichais seulement la valeur de la demi-période, en mS. Dans une deuxième version, après avoir rajouté le calcul, c'est la vitesse que j'affichais.

Ces deux expériences ont eu un résultat plutôt mitigé. La demi- période (ou dans la deuxième expérience, la vitesse) étaient très instables, bien que globalement cela tourne autour des valeurs attendues, mais avec très souvent des résultats aberrants.

Mes compétences en électronique sont limitées, mais j'imagine que c'est l'environnement perturbé du véhicule qui crée ces problèmes. Peut-être faut-il diminuer la valeur des deux résistances du pont pour augmenter l'intensité (si oui, jusqu'où aller)? L'arduino est alimenté par la prise allume-cigare, cela pose-t-il problème? Y a t-il des choses à faire?

Merci pour votre aide, et bonnes fêtes de fin d'année à tous.

Le principe du capteur est le bon, Fréquence = k x Vitesse.

Mais il est possible que tu loupes un cycle de temps en temps, si l'arduino est en train de faire "autre chose" (calculs dans un autre bout du programme) que de surveiller la broche au bon moment quand elle bascule.

J'opterai plutôt pour un convertisseur fréquence / tension, dédié à ça, puis lecture de tension (stable) par l'arduino pour rafraîchir un affichage périodiquement.

*Ou un circuit intégré "frequency to voltage converter" (type LM2907 http://www.ti.com/lit/ds/symlink/lm2907-n.pdf)

L'erreur peut aussi être causée par le fait que c'est la période et non la demi periode qui porte l'information. Tu pourrais essayer d'utiliser l'instruction pulsin() pour faire ta mesure. Il faut penser à mettre un hors temps afin de sortir de la fonction lorsque la voiture est à l'arrêt

Autre chose il serait prudent de mettre une zener pour protéger l'entrée de mesure.
Il faudra sans doute faire un filtrage sur le résultat car il y a toujours une petite fluctuation sur ce genre de mesure. Une moyenne glissante ou un filtrage en:
Mesure * a + résultat précédent * (1-a)
Peut régler le problème.

Pour répondre à Cristian_R:
Le convertisseur fréquence/tension est une bonne piste pour le futur de mon projet, pour l'instant je veux comprendre ce qui ne fonctionne pas dans le prototype.
Par ailleurs, l'arduino ne fait que deux choses, de façon répétitive: Calculer la durée d'une demi-période, entre le début d'un état haut et le début d'un état bas, puis afficher.
Comme je le précise, il n'y a aucune interruption qui pourrait perturber le fonctionnement.

Pour répondre à fdufnews:
C'est grâce à la trace de l'oscillo que j'ai déterminé que la demi-période fournissait une info exploitable.
Je ne connaissais pas pulsin(), d'après la référence, ça a l'air de faire exactement la même chose que ce que j'ai écrit.
J'avais bien pensé à faire une moyenne glissante, mais le risque est d'obtenir une mesure stable mais fausse.
Sinon, je retiens la protection par Zener.

Je reviens sur l'utilisation du convertisseur fréquence/tension. Sauf erreur de ma part, c'est une solution très utile pour fabriquer un compteur de vitesse ne faisant pas appel à de l'informatique embarquée, il suffit de brancher le convertisseur sur un appareil à cadran de type voltmètre, et c'est tout.

Dans mon cas, où je passe par l'arduino, je pense que cela ne ferait que compliquer le montage, et rajouter une marge d'erreur supplémentaire.

jeec:
Comme je le précise, il n'y a aucune interruption qui pourrait perturber le fonctionnement.

Si, à mon avis il y a un bug.
L'état du signal n'est pas connu au moment où on exécute ce bloc :

while(valBitInfoVitesse==LOW)
     {
          valBitInfoVitesse=digitalRead(bitInfoVitesse);
       }
t=micros();

Si on a la chance d'être sur LOW, tout ira bien, on attend bien pour sortir de la boucle le passage sur High.
Mais si on est déjà pendant un état HIGH du signal, ce bloc est sauté sans attendre.
La variable t est censée mémoriser en sortie de cette boucle l'instant précis d'un front montant.
Au lieu de ça elle va mémoriser un instant quelconque pendant un état HIGH.

Quand je fais:

while(valBitInfoVitesse==LOW)
     {
          valBitInfoVitesse=digitalRead(bitInfoVitesse);
     }

je ne fais qu'attendre un nouveau passage à l'état haut:

Soit je suis déjà à l'état haut (cet etat haut a déjà démarré, il ne m'interesse pas), et je ne rentrerai dans la boucle que quand l'état repassera à bas.
soit je suis à l'état bas, et je rentre dans la boucle.

C'est en sortant de la boucle, alors que l'état vient de passer à haut, que je commence mon comptage.

jeec:
cet etat haut a déjà démarré, il ne m'interesse pas

Il faut pourtant bien s'y intéresser, car il fixe quand même une valeur de t juste après cette boucle d'attente que l'on saute, avec une mauvaise valeur qui n'est pas celle d'un front.

Bon, je vais essayer d'expliciter un peu plus:

L'instruction "t=micros(); " est exécutée au moment du passage à l'état haut.
Il s'agit maintenant de se mettre en attente de la fin de cet état haut, ce que réalise la boucle:

 while(valBitInfoVitesse==HIGH)
     {
          valBitInfoVitesse=digitalRead(bitInfoVitesse);
     }

C'est le passage à l'état bas qui fait sortir de la boucle, et qui permet maintenant de calculer la demi-période (écart de temps entre le passage à l'état haut, soit t, et le passage à l'état bas, soit le temps présent ).

J'en reviens à mon problème de base, qui me semble quand même être lié à des perturbations CEM.

jeec:
L'instruction "t=micros(); " est exécutée au moment du passage à l'état haut.

Ben non, je persiste et signe.
Imagine que bitInfoVitesse soit HIGH dès le départ, et exécute méthodiquement ton code ligne par ligne.

valBitInfoVitesse=digitalRead(bitInfoVitesse);
     while(valBitInfoVitesse==LOW)
     {
          valBitInfoVitesse=digitalRead(bitInfoVitesse);
       }
      t=micros();

t=micros() sera exécuté sur le champ (on zape la boucle LOW) donc n'importe quand, sans même détecter ce passage à l'état haut.

le point de départ du chronométrage est faux, donc le deltaT qui suit juste derrière le sera aussi.

L'erreur est vraiment énorme, tellement que je ne la voyais pas. Je te remercie d'avoir insisté!

Je vais attendre l'état haut permettant de démarrer la mesure en faisant plutôt:

valBitInfoVitesse=digitalRead(bitInfoVitesse);
while(valBitInfoVitesse==HIGH)
     {
          valBitInfoVitesse=digitalRead(bitInfoVitesse);
     }
while(valBitInfoVitesse==LOW)
     {
          valBitInfoVitesse=digitalRead(bitInfoVitesse);
     }

Est ce que tu pourrais rééditer tes message et mettre le code entre les balises code (le bouton avec #)

Pour ton projet, pourquoi ne pas utiliser un optocoupleur et le mettre sur une pin d'interruption (il y a une limite en fréquence)

Salut,

Je viens de lire un peu tout ça, et ayant déjà pas mal planché sur ce genre de projet, quelques choses m'embête un peu...

Tu dis avoir une demi période de 10ms pour 40km/h, ce qui donnerait une impulsion tous les 22,222222... cm parcourus. Chez Renault (et certainement beaucoup d'autres), les capteurs vitesse utilisés sont donnés à une impulsion pour 20cm parcourus (ou encore 5 impulsions par mètre). Je pense que c'est ton cas (ça donne une erreur de 11% dans tes mesures, c'est possible). Par ailleurs, ce signal est très précis, donc mieux vaut être précis autant que possible.

Pour la mesure, j'utiliserais une méthode très différente, car l'arduino propose une fonction matérielle super puissante : "ICP1". Matériel veut dire que la gestion est purement électronique, aucun (ou presque) code n'intervient.

En gros, sur la pin 8, tu mets ton signal info vitesse, et avec un peu de config, chaque impulsion viendra mettre la variable vitesse à jour en utilisant une interruption. ICP (Input Capture Pin) a pour but de sauvegarder la valeur d'un compteur à chaque impulsion. Ce compteur est le timer 1 qu'il te suffit de configurer pour qu'il compte en continu, à une fréquence optimale (ça demande à réfléchir un peu, mais ça vaut largement le coup).

Tu pourras donc dans ton loop() faire simplement :

byte vitesse; // variable contenant la vitesse mesurée
void loop() {
  lcd.print(vitesse);
  delay(100); // mise à jour sur le LCD 10 fois par secondes, si trop rapide, ce sera illisible
}

Sachant que tu auras une ISR (Interrupt Sub Routine : fonction d'interruption) qui sera appelée à chaque impulsion et qui mettra à jour la variable vitesse.

Bonus : si tu utilises par exemple une variable de type long (long distance; par exemple), dans l'ISR, tu peux rajouter la ligne "distance++;". Ca mange pas de pain, mais du coup, tu auras un totaliseur kilométrique précis à 20cm... (kilométrage = distance / 5000;).

Ce n'est qu'une idée, mais j'aurais fait comme ça, car niveau exécution, c'est top et ça permet de rajouter un max de fonctions par la suite!

Pour ton projet, pourquoi ne pas utiliser un optocoupleur et le mettre sur une pin d'interruption (il y a une limite en fréquence)

Je n'ai jamais utilisé d'optocoupleur, je crois seulement savoir que ça permet de protéger un circuit de surtensions. Peux-tu m'indiquer quelle référence utiliser, et ou trouver l'information pour le montage? En ce qui concerne la limite de fréquence, tu veux dire qu'un optocoupleur pourrait ne pas laisser passer certaines fréquences?

Tu dis avoir une demi période de 10ms pour 40km/h, ce qui donnerait une impulsion tous les 22,222222... cm parcourus. Chez Renault (et certainement beaucoup d'autres), les capteurs vitesse utilisés sont donnés à une impulsion pour 20cm parcourus (ou encore 5 impulsions par mètre). Je pense que c'est ton cas (ça donne une erreur de 11% dans tes mesures, c'est possible). Par ailleurs, ce signal est très précis, donc mieux vaut être précis autant que possible.

J'étais bien conscient que les mesures que j'avais faites n'étaient pas bien précises (ce n'est pas facile en roulant!). Mon intention était de toutes façon d'étalonner le compteur avec un GPS, par la suite. Je vais utiliser tes paramètres, cela m'évitera probablement l'étalonnage.

Pour la mesure, j'utiliserais une méthode très différente, car l'arduino propose une fonction matérielle super puissante : "ICP1". Matériel veut dire que la gestion est purement électronique, aucun (ou presque) code n'intervient.

Je vais expérimenter tout ça.
Je vais être absent du forum quelque temps, mais vous tiens au courant de l'avancement à mon retour.

D'un coté ta voiture en 0-12V et de l'autre ton arduino en 0-5V entre deux du vide et un signal lumineux, comme ton signal est carré ça devrait marcher, il suffit d'adapter la résistance de la led de l'optocoupleur (voir la datasheet qui s'y rapporte).

Voir sous optocoupleur au milieu de la page :

http://www.reality.be/elo/labos2/interfacein.htm

Pour la fréquence, je parlais principalement de la capacité de l'arduino qui ne pourra mesurer au-delà d'un certaine fréquence... mais tes besoins devraient être largement en deça des essais que j'avais fait :

Les opto on également une vitesse de "réaction", voir également sur la datasheet.

La solution initiale que j'avais adoptée a fini par fonctionner, mais de façon non satisfaisante.
Le vitesse trouvée n'est pas stable, je pense que c'est parce qu'elle est déterminée de façon logicielle, et que la détection des fronts se fait avec un décalage du au temps d'exécution
des instructions.
J'ai expérimenté une autre solution, avec le compteur d'impulsions du Timer1, qui, dans son utilisation comme compteur d'événement externe, compte à partir de la broche 5. C'est donc sur cette broche que j'ai branché le fil info vitesse de ma voiture (toujours après adaptation par pont diviseur).
Là, j'ai une mesure identique à celle du compteur d'origine du véhicule (restera à faire une correction, avec un GPS, si on veut une mesure plus précise).

Je suis parti des données fournies par Super_Cinci:

les capteurs vitesse utilisés sont donnés à une impulsion pour 20cm parcourus

Sachant que 20cm = 0.2 m = 0.2*10e(-3) km

On en déduit que:
Pour 1 km, on aura 1/0.210e(-3) impulsions
Pour 1 km/h, on aura 1/0.2
10e(-3) impulsions pendant 1 h
Pour V km/h, on aura V/0.210e(-3) impulsions pendant 1 h
Pour V km/h, on aura V/0.2
10e(-3) impulsions pendant 3.610e6 mS
Pour V km/h, on aura V impulsions pendant 3.6
10e60.210e(-3)=740 mS

La valeur de la vitesse V sera donc égale au contenu du compteur après 740 mS de comptage.

Ce qui donne:

void setup()
{
	/* .... */
        
        //Initialisation du compteur 
        TCCR1A=0;
}

void loop() 
{
 //Démarrage du comptage
 bitSet(TCCR1B,CS12); // indique que l'horloge du compteur est externe (sur la broche 5)
 bitSet(TCCR1B,CS11); // Comptage sur front montant
 delay(740);  // délai en mS nécessaire pour compter un nombre d'impulsions égal à la vitesse
 TCCR1B=0; // arret du comptage
 ecrireNombre(TCNT1);
 TCNT1=0; //réinitialisation du compteur
}

Je n'ai pas encore expérimenté l'ICP1, je vous tiendrai au courant dès que j'aurai des résultats.

NB: Dans ma voiture, j'alimente l'arduino avec la prise allume cigare, et en conséquence, le régulateur de l'arduino chauffe très fort. Est-il possible de coller dessus un radiateur pour limiter la chauffe? Si oui, comment s'y prendre?

Merci à Jean-François pour les infos sur l'opto-coupleur