Eclairage à led de vélo 3W+tracker (atmega, ESP32) maker

Pour améliorer le filtrage précédant, il est possible de prendre un filtre d’ordre supérieur.

Mais, les livres pour bien comprendre les filtres numériques ne sont pas très vulgarisé et ils n’ont pas beaucoup d’exemple comme on a pourrait le voir sur le post précèdent :

http://www.f-legrand.fr/scidoc/docimg/numerique/filtre/filtrenum/filtrenum.html
il y a sur ce lien, sur le filtage avec Arduino
http://www.f-legrand.fr/scidoc/docimg/sciphys/arduino/filtrage2/filtrage2.html

Mais la vulgarisation n’est pas simple, car il y de nombreuses méthodes pour déterminer les coefficients d’un filtre numérique à partir d’un gabarit désiré ou à partir d’un filtre analogique.
Evidemment, il y a des softs qui déterminent les coefficients assez facilement tel que :
Matlab filterBuilder pour
Mathcad Plus avec des fonctions spécifiques
des soft en C Web Page Under Construction
Je n’ai pas réussi à faire fonctionner ce lien
jayduino - Arduino stuff written by me

Pas mieux avec celui la
http://t-filter.engineerjs.com/

Je n’ai pas de trouver de tuto sur le forum en anglais, ni en francais ????

Autant, le faire par soi meme....avec mathcad c'est assez facile
Mais pourquoi pas faire les calculs du filtre avec Excel, IDE arduino....
les calculs avec les complexes vont etre plus penible...mais why not....

Le calcul mathématique d’un butterworth numérique quel que soit son ordre va être présenté.
Dans le lien suivant, un article présente la méthode bilinéaire mais avec un filtre de coupure de 1 rad/s ???????? Ce qui n’aide pas à la compréhension.

Quelques soient l’ordre du filtre butterWorth, il n’y a pas de gain positif, le lien dans Wikipédia présente bien ce filtre analogique et il est très facile de déterminer la courbe d’atténuation avec Mathcad.
Nous allons prendre une fréquence de coupure de 10hz et une fréquence d’échantillonnage de 100Hz


Puis de déterminer les pôles du filtre pour les transformer en zéro du filtres numérique


Enfin de vérifier, l’atténuation du filtre avec les coefficients déterminés, la courbe de fréquence est tracé. On retrouve bien une atténuation de 0.707 ou de -3db à la fréquence de coupure.

Plus, l’écart entre la fréquence d’échantillonnage et la fréquence de coupure est grande, plus l’ordre N du filtre pourra être grand. mais plus le gain devra etre petit et la résolution des zeros devront etre precis.
pour bien tester le filtre numerique, il faut générer un signal sinusoïdal en entrée et voir en sortie l’atténuation pour chaque fréquence testée

sinon, en utilisant, la décomposition de série de fourriers d’un signal carré, L’amplitude de l’ondulation sera égale à l’équation suivante exemple avec une frèquence de coupure de 1hz.

Si on utilise, un ordre 1, alors l’ondulation theorique sera de 14%

Si on utilise un ordre 3, ondulation sera de 0.1%

Maintenant, il reste à implanter dans l’Arduino.
Evidement les coefficients a(z) sont en float car il faut une certaine précision.
Quel est le temps de calcul de l’Arduino en fonction de l’ordre ?
Mais ce sera pour plus tard.

Précédemment, pour un filtre butteworth d’ordre 3, avec une fréquence d’échantillonnage de 100z et de fréquence de coupure de 10hz,
Si l’on choisit les coefficients au centième près par défaut, le gain passe 1/55 à 1/53 et la courbe d’atténuation fréquentielle sera légèrement modifiée avec une fréquence de coupure légèrement décalé

Le temps de calcul du filtre avec l’ATMEL 328 est de 120us pour un troisième ordre
J’aurai pensé que le temps de calcul aurait été bien plus long. le compilateur doit faire des simplifications

Voici les résultats simuler avec Matlab dans un premier temps avec une fréquence d’échantillon de 100Hz, un signal carré de 10hz, et une fréquence de coupure de 10hz donc avec 10 échantillons par période du signal, ce n’est pas terrible


Si l’on essaye de faire avec une fréquence d’échantillon de 1000hz, il y a une divergence du signal de sortie même avec 3 chiffres ou 4 chiffres après la virgule des coefficients zéros du filtre.
C’est là que l’on voit qu’entre la théorie et la pratique, il y a un monde
Plus l’ordre est élevé et plus s'il y a un écart entre fe et fc et plus il faudra une précision sur les coefficients du filtre.
Remarque : Lorsqu’il y a un trop grand écart entre le gain statique avec les coefficients pris par défaut ou avec des coefficients avec 7 chiffres significatifs alors le filtrage devient divergent.

Donc, nous allons le faire avec un butterworth numerique du deuxième ordre
Voici les valeurs pour une fréquence d’échantillonnage de 1000Hz et une fréquence de coupure de 10hz.


La simulation dans Matlab, avec ces coefficients de ce deuxième ordre et un gain de 1/1000. Donne la courbe suivante. Ce signal de sortie a 100 valeurs par période donc il est moins échantillonné que le précèdent.

Toujours avec les mêmes coefficients que précèdent mais pour une fréquence de coupure de 1Hz avec une fréquence d’échantillonnage de 100Hz et toujours notre signal carré à 10Hz, on peut observer sur la figure suivante que l’ondulation de la sortie est bien de 13decimal comme en théorie et on garde bien la valeur moyenne.

Voici les résultats avec l’Arduino, Le temps de calcul du filtre 2 ordre est de 80us
Voici le programme,

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <avr/wdt.h>   //chien de garde



#define LED13    13       
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables


unsigned    int    temps=0; 

            float  entree=0;
            float  entree1=0;
            float  entree2=0;
         
            float  sortie=0;
            float  sortie1=0;
            float  sortie2=0;
            
            float  out=0;
 const float b1 =2;
 const float b2 =1;

             
            const float a1 =-1.911;
            const float a2 =0.915;
            
            const float gain =1000;   
                    

void setup() {
  pinMode(LED13, OUTPUT);

  Timer1.initialize(10000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(16, 2);                   //modifier pour un afficheur 20x4
  Serial.begin(9600); 

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 10  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
}



// Interruptions  tous les 0.01s fait par le timer1***********************************
void callback()  {

temps++;  
if (temps>4)         //1=singnal carré de 0.1s  5*routine d'interruption*2
{
temps=0;  //reset : attention cette boucle doit etre infereir à son temps d'execution
if (entree==1023) {entree=0;}  else {entree=1023;}  
 
}//temps>1


      /* ----filtre passe pas Butterwoth 3émé ordre  pour fc=10hz,  fechantillon=1000Hz------   */
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
entree2=entree1;      //entree(n-2)
entree1=entree;       //entree(n-1)

sortie2=sortie1;      //sortie(n-2)
sortie1=sortie;     //sortie(n-1)

//sortie=entree+entree1*b1+entree2*b2+entree3*b3-sortie1*a1-sortie2*a2-sortie3*a3 ;   //filtre passe pas recursif ordre 3
sortie=(entree+entree1*b1+entree2*b2-sortie1*a1-sortie2*a2) ;   //filtre passe pas recursif ordre 2
out=sortie/gain;       

digitalWrite(LED13,LOW);
//if ( digitalRead(LED13)== 1 ) {digitalWrite(LED13,LOW);}  else {digitalWrite(LED13,HIGH);}

  Serial.print(temps);
  Serial.print(";");
  Serial.print("\t");
    
  Serial.print(entree);
  Serial.print(";");
  Serial.print("\t");
  
  Serial.print(out);
  Serial.print("\t");
  Serial.println(";");  //mise à la ligne

}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main 
void loop() {  
  lcd.setCursor(0,0); 
  lcd.print(temps);


   
} // fin loop

La sortie de l’Arduino correspond bien à la théorie

En synthèse : avec ce filtre RII du deuxième ordre filtre qui est bien mieux que le premier ordre alors extraire l'information de notre capteur accélérateur en atténuant les vibrations de la route est plus efficace.

Encore pour tester le filtre, on aurait pu utiliser le traceur série pour tester le filtre du deuxième ordre numérique, mais on peut aussi utiliser une sortie PWM.

En effet, si l’on désire lire le signal carré ou un signal alternatif est connaitre les résultats du filtre numérique, il est possible d’utiliser une sortie PWM filtré analogiquement par un RC.
Avec une fréquence PWM de 32kHz, et un RC de (10kohms et une capacité de 33nF) donc une fréquence de coupure de 482Hz atténuera l’ondulation de la PWM de
On peut observer l’ondulation pour un rapport cyclique de 50% sur la figure suivante :

Cette ondulation est de 0.075V autour de la valeur moyenne.

Avec un signal sinusoïdal ou carré de 10Hz et la fréquence de coupure de 1Hz, on retrouve bien que la valeur moyenne du signal carré qui est donc bien filtré en réel et dans ISIS.

Mais si l’on met un signal sinusoïdale de fréquence de 1hz, l’atténuation est de 0.35 à la place de 0.7 par contre le déphasage est bien de -90°.

Evidemment pour une fréquence sinusoïdale de 0.1Hz, il n’y a plus d’atténuation et le déphasage est presque à 0°.

Le code avec la pwm et la conversion analogique est le suivant :

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <avr/wdt.h>   //chien de garde


#define PWM3   3      //   timer2    
#define LED13    13       
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables


unsigned    int    temps=0; 

            float  entree=0;
            float  entree1=0;
            float  entree2=0;
         
            float  sortie=0;
            float  sortie1=0;
            float  sortie2=0;
            
            float  out=0;
 const float b1 =2;
 const float b2 =1;

             
            const float a1 =-1.911;
            const float a2 =0.915;
            
            const float gain =1000;   
                    

void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(10000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(16, 2);                   //modifier pour un afficheur 20x4
  Serial.begin(9600); 

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 10  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

  
}



// Interruptions  tous les 0.01s fait par le timer1***********************************
void callback()  {

/*
temps++;  
if (temps>4)         //1=singnal carré de 0.1s  5*routine d'interruption*2
{
temps=0;  //reset : attention cette boucle doit etre infereir à son temps d'execution
if (entree==1023) {entree=0;}  else {entree=1023;}  
 
}//temps>1
*/

      /* ----filtre passe pas Butterwoth 3émé ordre  pour fc=10hz,  fechantillon=1000Hz------   */
entree=analogRead(A0);         
//digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
entree2=entree1;      //entree(n-2)
entree1=entree;       //entree(n-1)

sortie2=sortie1;      //sortie(n-2)
sortie1=sortie;        //sortie(n-1)

//sortie=entree+entree1*b1+entree2*b2+entree3*b3-sortie1*a1-sortie2*a2-sortie3*a3 ;   //filtre passe pas recursif ordre 3
sortie=(entree+entree1*b1+entree2*b2-sortie1*a1-sortie2*a2) ;   //filtre passe pas recursif ordre 2
out=sortie/gain;       //plus facile de diviser  que de multiplier


out = map(out, 0, 1023, 0, 255);   //mise à l'echelle

analogWrite(PWM3,out); 


//digitalWrite(LED13,LOW);
if (entree>=850) {digitalWrite(LED13,HIGH);}  else {digitalWrite(LED13,LOW);}  //test de la lecture du signal

/*  Serial.print(temps);
  Serial.print(";");    //creation du fichier CSV
  Serial.print("\t");
*/    
  Serial.print(entree);
  Serial.print(";");
  Serial.print("\t");
  
  Serial.print(out);
  Serial.print("\t");
  Serial.println(";");  //mise à la ligne dans le terminal

}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main 
void loop() {  
  lcd.setCursor(0,0); 
  lcd.print(entree,0);
   lcd.print("   ");

   
} // fin loop

Réalisation complète et finie du typon sur ares :

Les 2 cartes arduino doivent venir s'empiler l'une sur l'autre

La carte à été réalisée à partir du schémas isis de base, légèrement modifé :

En effet j'ai modifié les entrées et sorties au niveau des ports arduino dans le schéma isis, pour relier tous les composants entre eux sur une même carte arduino sur ares. (Aucun composant devait être relié d'une carte à l'autre car les cartes s'empilent)
Voici les ports affectés aux cartes arduino :

Remarquez que seul un fil est présent entre les 2 cartes, il s'agît de la broche 4 de la première carte qui doit être reliée au composant U1, il suffit de relier U1 à la broche 4 de la deuxième carte puisque les cartes s'empilent.

Le reste des différents fils non reliés doivent simplement être reliées par un pont.

J'ai également rajouté une résistance R10 en parallèle à R20 pour éviter une surchauffe de R20.

Enfin, remarquez que je n'ai mis que la led D10 par manque de place sur la carte arduino sur ares.

super

MAINTENACE ARDUINO NANO

lorsqu'on alimente l'Arduino nano sous 5V ça marche mais lorsqu'on l'alimente en USB cela ne fonctionne pas.
Quel est le composant dans le schéma qui ne fonctionne plus ?

donc, ce n'est pas le régulateur 5V qui est est incriminé car cela fonctionne en 5V
Mais l'erreur se trouve au niveau de la diode shotky D1 alimentée par l'USB.

Cette diode D1 se trouve sur le PCB (Printed Circuit Board)

mais, on ne voit pas correctement ou est l'anode et la cathode sur le composant
donc, on a identifié l'anode avec la mesure de la tension d'entrée VUSB à 5v.
La diode MBR0520 supporte 0,5 A, donc nous l'avons remplacé par un boitier sod 123 supportant 0,3A

nous avons fait un petit programme pour vérifier le bon fonctionnent des sorties.
Celle -ci passe de 0 à 5V toutes les 5secondes pour pouvoir vérifier avec un multimètre les différentes tensions
programme

/*

  • SANKHE Abdou
    Clignotement de LED
    Allume toues les LED pendant 5 seconde, puis les éteint pendant 5 seconde
    puis le programme se répète indéfiniment

Cet exemple est dans le domaine public
Traduction française par X. HINAULT - www.mon-club-elec.fr
*/
#define Led13 13
#define Led12 12
#define Led11 11
#define Led10 10
#define Led9 9
#define Led8 8
#define Led7 7
#define Led6 6
#define Led5 5
#define Led4 4
#define Led3 3
#define Led2 2
#define LedA0 A0
#define LedA1 A1
#define LedA2 A2
#define LedA3 A3
#define LedA4 A4
#define LedA5 A5
#define LedA6 A6
#define LedA7 A7
// Définition des broches

void setup() {
// initialise la broche 13 en sortie numérique
// la broche 13 a une LED déjà connectée sur la plupart des cartes Arduino :
pinMode(13, OUTPUT);
pinMode(12,OUTPUT);
pinMode(11,OUTPUT);
pinMode(10,OUTPUT );
pinMode(9,OUTPUT );
pinMode(8,OUTPUT);
pinMode(7,OUTPUT);
pinMode(6,OUTPUT);
pinMode(5,OUTPUT);
pinMode(4,OUTPUT);
pinMode(3,OUTPUT);
pinMode(2,OUTPUT);
pinMode(A0,OUTPUT);
pinMode(A1,OUTPUT);
pinMode(A2,OUTPUT);
pinMode(A3,OUTPUT);
pinMode(A4,OUTPUT);
pinMode(A5,OUTPUT);
pinMode(A6,OUTPUT);
pinMode(A7,OUTPUT);

}

void loop() {
digitalWrite(Led13, HIGH);
digitalWrite(Led12, HIGH);
digitalWrite(Led11, HIGH);
digitalWrite(Led10, HIGH);
digitalWrite(Led9, HIGH);
digitalWrite(Led8, HIGH);
digitalWrite(Led7, HIGH);
digitalWrite(Led6, HIGH);
digitalWrite(Led5, HIGH);
digitalWrite(Led4, HIGH);
digitalWrite(Led3, HIGH);
digitalWrite(Led2, HIGH); // allume la LED

digitalWrite(LedA0, HIGH);
digitalWrite(LedA1, HIGH);
digitalWrite(LedA2, HIGH);
digitalWrite(LedA3, HIGH);
digitalWrite(LedA4, HIGH);
digitalWrite(LedA5, HIGH);
digitalWrite(LedA6, HIGH);
digitalWrite(LedA7, HIGH);

delay(5000); // ne fait rien pendant 1 seconde
digitalWrite(13, LOW);
digitalWrite(12,LOW);
digitalWrite(11,LOW);
digitalWrite(10,LOW );
digitalWrite(9,LOW );
digitalWrite(8,LOW );
digitalWrite(7,LOW );
digitalWrite(6,LOW);
digitalWrite(5,LOW );
digitalWrite(4,LOW );
digitalWrite(3,LOW );
digitalWrite(2,LOW );

digitalWrite(A0,LOW );
digitalWrite(A1,LOW );
digitalWrite(A2,LOW );
digitalWrite(A3,LOW);
digitalWrite(A4,LOW );
digitalWrite(A5,LOW );
digitalWrite(A6,LOW );
digitalWrite(A7,LOW );

// éteint la LED
delay(5000); // ne fait rien pendant 1 seconde
}

schéma de simulation

on peut observer que la sortie analogique A6 et A7 ne peuvent pas être commandé en numérique.

malgré que l'on soit à vide , la tension de sortie est seulement de 4.3V à cause de la chute de tension interne du transistor.

les tensions sont les suivantes :


Fonctionnement du MPU6050

Le capteur MPU6050 est la combinaison d’un accéléromètre et d’un gyroscope. Il est aussi doté d’un capteur de température que nous ne servirons pas ici. L’objectif était d’éteindre la l’éclairage lorsque le vélo ne bougeait plus pendant au moins 2 minutes (je me suis aussi amusé à faire le cas inverse ou lorsque le vélo bouge on allume). Cela nous permet d’avoir une meilleure autonomie et une meilleure gestion des batteries. Pourquoi avoir choisis le MPU6050 ? Nous utilisons le matériel que nous avons dans notre université, nous essayons de « recycler » un maximum pour éviter de faire des dépenses alors que nous pouvons utiliser d’autres composants.
Voici le programme que j’ai fait :

Nous utilisons seulement l’accéléromètre du MPU6050, j’ai commencé par trouver par quel coefficient il fallait diviser la mesure pour obtenir quelque chose de cohérent, sur internet et les différents tutos explicatifs que l’on peut trouver personne ne parle de ce coefficient, j’ai donc cherché dans la datasheet et en regardants les différents paramètres que j’avais sur mon MPU6050, il s’avère qu’il fallait que je choisisse 16384. Ensuite j’ai effectué une correction à chaque démarrage ou redémarrage du module pour que son accélération d’origine soit 0,0,0 lors de son démarrage car nous devions tenir compte de l’inclinaison du vélo et la force gravitationnelle de 1G vers le centre de la terre. Par la suite j’ai calculé le module de l’accélération et j’ai fais en sorte que lorsque que l’on marche normalement à une vitesse d’environs 6km/h le module est d’environs 12/13, alors dans le programme lorsque le module est supérieur a 10 il active une boucle qui fait clignoter une led (cette même boucle sera remplacée par l’extinction de là l’éclairage du vélo). Pour trouver le module de l’accélération lorsque l’on marche nous avons dû effectuer des test e utilisant un Arduino nano, un écran LCD, le MPU6050 et 2 batteries lithium 18650.

J'ai vérifié la mesure du courant du processeur de l'arduino PRO MINI ainsi que celui de l'arduino NANO, les deux possèdent un processeur ATMEGA 328.

Pour réaliser cette mesure sur nous allons mettre une résistance 100 Ohms brancher en série sur l'alimentation et de l'autre côté sur l'entrée Vcc (pour l'arduino PRO MINI) ou sur l'entrée Vin (pour l'arduino NANO)
Ensuite nous allons mesurer la tension aux bornes de la résistance, puis nous allons appliquer la loi d'Ohms: I = U/R

Pour la PRO MINI nous allons réaliser les mesures en utilisant les deux entrées l'entrée VCC et l'entrée RAW.

Voici les entrées RAW et VCC représenter sur le schéma de la carte arduino PRO MINI

Voici les entrées RAW et VCC représenter sur le diagramme de la carte arduino PRO MINI

Pour l'arduino PRO MINI en 16MHz alimenter sur VCC avec la led d'alimentation qui demande :

  • avec une alimentation de 3.3V, la consommation est de 1.9 mA, sachant que la led alimentation consomme 0.15mA
  • avec une alimentation de 5V, la consommation est de 9.7 mA, sachant que la led alimentation consomme 0.32mA

Pour l'arduino PRO MINI avec l'alimentation utilisant le régulateur de tension brancher sur l'entrée RAW, ce port peut être alimenté de 7V à 12V.

avec une alimentation de 7.5V, la consommation est de 13 mA, sachant que la led alimentation consomme 0.57mA
avec une alimentation de 10V, la consommation est de 21 mA, sachant que la led alimentation consomme 0.82mA

Pour l'arduino NANO alimenter sur VIN on a avec une alimentation de 5V une consommation de 23 mA.

J'ai réalisé le schéma ISIS ci-dessous représentant un hacheur.

Ce montage est composé de resistances allant de 1 ohm à 100 kohms, d'un transistor TIP 122, d'une diode Schottky (PBYR745), d'une inductance de 0.5 mH, d'une led de puissance et d'un mosfet IRF 9530.

Voici le montage final avec la platine d'essai :

La PWM est généré par un signal carré de fréquence 32 KHZ
Le hacheur est alimenté à 8.4 V
j'ai préferer utiliser un mosfet IRF9530 au lieu d'un transistors TIP 122 car une mosfet à une haute impédance et ne demande qu'un petit courant pour s'allumer.

L'utilisation de la diode schottky fait office de protection des autres composants. Elle a un temps de commutation trés court ce qui lui permet d'être trés réactive.

Suite à la commande de la LED avec le CI FL7760 de mar 19 2019 précèdent

On souhaite réaliser un programme pour augmenter le rapport cyclique de la PWM, de 0% à 100% avec un pas de 10%, par l’appui d’un bouton poussoir. Ce circuit demande une pwm de dimming minimale de 2kHz.

Mais comment gérer l’incrément de cette PWM avec le bouton poussoir ?

Pour vérifier notre programme, nous l’avons simulé avec ISIS, avec une carte Arduino Nano, donc nous avons utilisé le pin 3 pour la PWM et le pin 2 pour notre bouton poussoir.

Dans le setup de notre programme, on déclare le pin connecté au bouton poussoir comme une entrée et la PWM comme une sortie (voir figure ci-dessous).

Fonction Loop :

Nous avons déclaré et initialisé un boolean « relâcher_bouton » qui nous permettra d’augmenter l’intensité lorsque on relâche le bouton poussoir.
La variable intensité ne peut que varier de 0 à 255 car à « analogWrite() » avec Atmel 328 est sur 8bit. Donc pour cela nous avons créé une condition : si l’intensité est supérieure à 255 on revient à 0 qui permettra d’éteindre la LEDde puissance.
Au niveau de l’affichage, on affiche la valeur de la variable pourcentage, cette variable reçoit la valeur de l’intensité en pourcentage, en sorte que analogWrite (PWM,255) demande un cycle de travail de 100 % et analogWrite(PWM,127) correspond à un cycle de travail à 50%.

Voici le programme de la fonction loop :

void loop() {  
  lcd.setCursor(0,0); // position du cousor en haut à gauche 
  analogWrite(3, intensite);    //on envoie sur le pignal PWM la valeur de l'intensité
  lcd.print("Duty cycle ");lcd.print(pourcentage);lcd.print("%  "); // affichage du rapport cyclique
  boolean relacher_bouton= false; 
    
  if(digitalRead(bouton)==HIGH){ // si on appuie sur le bouton alors
    while(!relacher_bouton){      // on rentre dans cette boucle jusqu'à ce qu'on relâche le bouton
      if(digitalRead(bouton)==LOW){ // si on relâche le bouton alors 
        relacher_bouton=true; // relâcher_bouton est vrai et
        intensite=intensite+25.5; // dès qu'on rêlache le bouton, on augmenté l'intensité
        if (intensite > 255){ // si le rapport cyclique arrive à 255, soit 100% alors
          intensite=0; //Reviens à 0
        }      
        pourcentage = ((100*intensite)/255); //formule pour afficher le rapport cyclique en pourcentage
      }
    }
  }
}

Voyons la simulation sur ISIS, on remarque bien que pour un rapport cyclique de 30 %, nous avons 1,8 carreaux à 5V sur 6 ce qui correspond à 0,3=1.8/6 donc 30%.

![](http://<a target=)

">

De même, pour un rapport cyclique de 90%, on remarque que 5,4 carreaux sont à 5V sur 6 ce qui corresponde à 0,9=5.4/6 donc 90%

![](http://<a target=)

">

Ci-dessous le programme complet réalisé sur Arduino

#include <LiquidCrystal.h>

int bouton = 2;  //init bouton 
float intensite=0;
int pourcentage=0;
LiquidCrystal lcd(9,8,4,5,6,7); 

void setup() {
  pinMode(3, OUTPUT); // PWM en sortie 
  pinMode(bouton, INPUT); // Bouton en entrée
  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 10  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet                                       
}

void loop() {  
  lcd.setCursor(0,0); // position du cousor en haut à gauche 
  analogWrite(3, intensite);    //on envoie sur le pignal PWM la valeur de l'intensité
  lcd.print("Duty cycle ");lcd.print(pourcentage);lcd.print("%  "); // affichage du rapport cyclique
  boolean relacher_bouton= false; 
    
  if(digitalRead(bouton)==HIGH){ // si on appuie sur le bouton alors
    while(!relacher_bouton){      // on rentre dans cette boucle jusqu'à ce qu'on relâche le bouton
      if(digitalRead(bouton)==LOW){ // si on relâche le bouton alors 
        relacher_bouton=true; // relâcher_bouton est vrai et
        intensite=intensite+25.5; // dès qu'on rêlache le bouton, on augmenté l'intensité
        if (intensite > 255){ // si le rapport cyclique arrive à 255, soit 100% alors
          intensite=0; //Reviens à 0
        }      
        pourcentage = ((100*intensite)/255); //formule pour afficher le rapport cyclique en pourcentage
      }
    }
  }
}

Communication avec Arduino:

Le MPU6050 utilise le protocole de communication I²C dont la connexion est réalisée par l'intermédiaire de deux lignes :
• SDA (Serial Data Line) : ligne de données bidirectionnelle
• SCL (Serial Clock Line) : ligne d'horloge de synchronisation bidirectionnelle
Dans le monde Arduino, il existe une librairie dédiée à ce protocole : Wire.
En lisant la doc de la librairie Wire Arduino, on voit que sur un Arduino NANO ce sont les entrées analogiques A4 et A5 qui correspondent respectivement à SDA et SCL. On câble donc tout cela ensemble.
Le MPU a besoin d'être alimenté en 3.3V, on relie donc la broche Vcc à la broche 3.3V de l'Arduino et le GND au GND de l'Arduino.
Ce qui nous donne au final ce câblage :

Configuration :

Sensibilité de l’accéléromètre :

Le registre ACCEL_CONFIG permet de configurer la plage de fonctionnement de l'accéléromètre.

Une plage de ±2g est suffisante pour l'étude, ce qui nous amène à utiliser le facteur AFS_SEL = 0

Structure du registre ACCEL_CONFIG

La paramètre AFS_SEL est codé sur les bits 3 & 4. La valeur 0 en binaire s'écrit alors 0 aussi.
Les bits 0, 1 et 2 sont réservés, on les laisse donc à 0.
Les bits 5, 6, 7 servent à effectuer un auto-test des axes X Y Z respectivement. Cela ne nous intéresse pas, on les laisse donc à 0.

Plus loin dans la datasheet, on arrive sur la page du registre Power Management. Ce registre nous permet de choisir la source d'horloge que le MPU va utiliser au travers du paramètre CLKSEL.

Nous allons utiliser ici son horloge interne de 8MHz, soit CLKSEL = 0.

Structure du registre PWR_MGMT_1

Là encore, on voit que ce registre est codé sur 8 bits.
CLKSEL est codé sur les bits 0, 1 & 2. La valeur 0 en binaire s'ecrit alors 000
Le bit 3 TEMP_DIS (temparture disabled) sert à désactiver la lecture de la température lorsqu'il est à 1. On va garder cette information, on laisse donc ce bit à 0.
On laisse le reste à 0, ce qui nous donne au final 00000000 en binaire, soit 0x00 (hex).
Enfin, l'adresse du registre est 0x6B en hexadécimal.
Ce qui nous donne le code Arduino suivant :

Wire.beginTransmission(MPU);        // Start communication with MPU6050 // MPU=0x68
Wire.write(0x6B);                  // Talk to the register 6B
Wire.write(0x00);                  // Make reset - place a 0 into the 6B register

Filtre passe bas :

Le registre, de nom CONFIG, va nous servir à configurer la fréquence de coupure du filtre passe-bas de l'accéléromètre au travers du paramètre DLPF_CFG.

Digital Low Pass Filter

On va partir sur une fréquendce d'environ ~43Hz, ce qui nous amène à DLPF_CFG = 3.
On laisse les autres bits à 0, ce qui nous amène à en binaire, soit 0x03 en hexadécimal.
Enfin, l'adresse du registre est 0x1A en hexadécimal.
Ce qui nous amène au code Arduino suivant :

Wire.write(0x1A);             // Request the CONFIG register
Wire.write(0x03);             // Apply the desired configuration to the register : DLPF about 43Hz

Fréquence d’horloge :

Plus loin dans la datasheet, on arrive sur la page du registre Power Management. Ce registre nous permet de choisir la source d'horloge que le MPU va utiliser au travers du paramètre CLKSEL.

Nous allons utiliser ici son horloge interne de 8MHz, soit CLKSEL=0

Structure du registre PWR_MGMT_1
Là encore, on voit que ce registre est codé sur 8 bits.
CLKSEL est codé sur les bits 0, 1 & 2. La valeur 0 en binaire s'ecrit alors 000.
Le bit 3 TEMP_DIS (temparture disabled) sert à désactiver la lecture de la température lorsqu'il est à 1. On va garder cette information, on laisse donc ce bit à 0.
On laisse le reste à 0, ce qui nous donne au final 00000000 en binaire, soit 0x00 (hex).
Enfin, l'adresse du registre est 0x6B en hexadécimal.
Ce qui nous donne le code Arduino suivant :

Wire.beginTransmission(MPU_ADDRESS); // Start communication with MPU
Wire.write(0x6B);                    // Request the PWR_MGMT_1 register
Wire.write(0x00);                    // Apply the desired configuration to the register
Wire.endTransmission();              // End the transmission

LECTURE DES DONNEES BRUTES DU MPU6050 :

Maintenant que le MPU6050 est câblé et configuré, il ne reste plus qu'à lire les données brutes du capteur.

On constate dans la table des registres que les registres 59 à 64 stockent les valeurs de sorties de l'accéléromètre .

On va donc simplement parcourir le contenu de ces 6 registres, en commençant à l'adresse 0x3B :

Wire.beginTransmission(MPU_ADDRESS);// Start communicating with the MPU-6050
Wire.write(0x3B);                   // Send the requested starting register
Wire.endTransmission(false);             // End the transmission
Wire.requestFrom(MPU_ADDRESS,6);   // Request 14 bytes from the MPU-6050

Mesure de l’accélération :

Un accéléromètre mesure l'accélération subite et l'exprime en g.
1g représente l'accélération de pesanteur sur Terre, soit environ 9,8m.s−29 et des bananes.
L'accéléromètre au repos mesure donc une accélération de 1g.

Script intégral en C sur l'IDE Arduino :

#include // include la bibliothèque Wire.h

#define MPU_ADDRESS 0x68  // I2C address of the MPU-6050

#define X  0        // X axis
#define Y  1        // Y axis
#define Z  2        // Z axis
float acc_total_vector;

int acc_raw[3] = {0,0,0};

/**
* Configure la plage de fonctionnement de l'accéléromètre :
*  - accéléromètre: +/-2g

*
* @return void
*/
void setup() {
analogReference(EXTERNAL);
Serial.begin(19200);
Wire.begin();  
// Configure power management
Wire.beginTransmission(MPU_ADDRESS); // Start communication with MPU
Wire.write(0x6B);                    // Request the PWR_MGMT_1 register
Wire.write(0x00);                    // Apply the desired configuration to the register
Wire.endTransmission();              // End the transmission

// Configure the acceleromter's sensitivity
Wire.beginTransmission(MPU_ADDRESS); // Start communication with MPU
Wire.write(0x1C);                    // Request the ACCEL_CONFIG register
Wire.write(0x00);                    // Apply the desired configuration to the register : ±8g
Wire.endTransmission();              // End the transmission

// Configure low pass filter
Wire.beginTransmission(MPU_ADDRESS); // Start communication with MPU
Wire.write(0x1A);                    // Request the CONFIG register
Wire.write(0x00);                    // Set Digital Low Pass Filter about ~43Hz
Wire.endTransmission();              // End the transmission

Wire.endTransmission(true);        //end the transmission
}

void loop() {
Wire.beginTransmission(MPU_ADDRESS);// Start communicating with the MPU-6050
Wire.write(0x3B);                   // Send the requested starting register
Wire.endTransmission(false);             // End the transmission
Wire.requestFrom(MPU_ADDRESS,6);   // Request 14 bytes from the MPU-6050

// Wait until all the bytes are received
while(Wire.available() < 6);

acc_raw[X]  = (Wire.read() << 8 | Wire.read())/ 16384.0; // Add the low and high byte to the acc_raw[X] variable
acc_raw[Y]  = (Wire.read() << 8 | Wire.read())/ 16384.0; // Add the low and high byte to the acc_raw[Y] variable
acc_raw[Z]  = (Wire.read() << 8 | Wire.read())/ 16384.0; // Add the low and high byte to the acc_raw[Z] variable

acc_total_vector = sqrt(pow(acc_raw[X], 2) + pow(acc_raw[Y], 2) + pow(acc_raw[Z], 2));

Serial.print(acc_raw[X]);
Serial.print("/");
Serial.print(acc_raw[Y]);
Serial.print("/");
Serial.print(acc_raw[Z]);
Serial.print("/");
Serial.println(acc_total_vector);
delay(10);

}

Résultats moniteur Arduino:

1g suivant +X :

1g suivant -Y :

1g suivant +Z:

Conclusion:

Quant aux différents filtres, j'ai utilisé le filtre passe-bas numérique intégré dans le MPU6050 avec une bande passante d'environ 45Hz, quand j'essaie de bouger le capteur pour voir les réponses suivant plusieurs axes, les réponses sont quasi-stables dans le moniteur série d'IDE Arduino, le fait d'implanter des équations récurrentes de différents types de filtre (pass_bas pass_haut passe bande ...), c'est facile, mais je n'y vois pas l’intérêt, car tous les résultats qu'on trouve sur le moniteur série Arduino, sont soit 1g suivant X soit 1g suivant Y soit 1g suivant Z.

Allumer un feu stop après un freinage ou un arrêt normal du vélo, est tout à fait possible avec un accéléromètre détectant la décélération, toutefois cela nécessite une étude comparatives de plusieurs accéléromètres analogiques et numériques, sans oublier les autres solutions technologiques pour répondre à cette problématique, comme par exemple un compteur de vitesse (Tr/min)

Pourquoi ne pas avoir utilisé une bibliothèque dédiée pour le MPU 6050 ??????
Mais en général, avec ces bibliothèques, la configuration est difficile….
donc c’est vraiment bien, ce que vous avez fait.

Est-ce que vous avez fait des essais du capteur MPU6050 dans une voiture et vérifier les dynamiques de l’accélération avec une application smartphone ???????

si tu mets

[url=https://servimg.com/view/19537397/16]![](upload://zOeV8ZrVOw3wVVFDD4nES6nTVke.png)[/url]

on verra l'image

Bref, les compteurs de vélos (odomètre capteur magnet KY03) qui mesurent la vitesse grâce à un compte tour de la roue pourrait aussi mesurer la décélération et activé un STOPT arrière.
Mais quelle pourrait être la précision de cette méthode ? Faut-il mettre plusieurs aimants sur la roue ? Faut-il filtrer la valeur de la décélération ?

De plus, il existe des éclairages arrière vélos qui donnent la direction (clignotant droit et gauche) sans fil, mais sur un vélo, il n’y qu’à tendre la main à droite ou à gauche donc c’est inutile.
Mais pour un velomobile (vélo caréné), cela est intéressant


Voici un bel article avec 2 arduinos et une communication bluetooth
https://create.arduino.cc/projecthub/simonwongwong/bluetooth-enabled-bicycle-turn-signal-2f4f5d
Mais les matrices de leds ne permettent pas d’être vu de jour (pas assez de lumen)

L’article suivant utilise une télécommande 4 boutons

Donc une télécommande et un récepteur, voici en voici l’électronique
http://www.wzmicro.com/rf.htm
Cela ne coute rien, mais il faudra mettre le compteur de vélo sur l’Arduino arrière pour avoir un feu stop

Donc, un compteur vélo avec Bluetooth va être réalisé et commander l’éclairage arrière comme on peut l’observer sur la figure suivante.
Le compteur avec son afficher a permis de vérifier le bon fonctionnement de la programmation et s’il fallait filtrer la mesure de la décélération
Il faudra prendre des Arduino Pro et pas nano pour leurs faibles consommations sur la batterie.
Mais pour vérifier la programmation le nano a été utilisé.

L’indication droite et gauche est évidemment clignotante ainsi que le stop et lors de la décélération.
Mais on aurait pu mettre une 4éme led, pour un feu stop mais cela n’a pas été fait.
Le bouton poussoir stop est inutile, puisque la décélération est mesurée et active automatiquement l’éclairage arrière.

Le compteur vélo a 4 aimants sur la roue pour augmenter la précision des mesures de la vitesse dans les basses vitesses.
Avec l’utilisation de la mesure en milliseonde à chaque quart de roue
L’erreur de la vitesse est déterminée par l’équation suivante avec 4 aimants
DeltaVitesse=(2010*3.6/4)/(Timer+1)( Timer) pour Timer=25ms=>2.78km/h pour vitesse 72km/h
La précision correspond à l’équation suivante
Précision=1/(Timer+1) donc à 72km/h précisons de 3.8% et à 18km/h 1%

Etant donné que l’accélération est déterminée aussi pour chaque quart de roue tournée, l’erreur de l’accélération correspondra aux équations suivantes
DeltaAccelera=(277)/(Timer+1)( Timer) pour Timer=25ms=>0.42m/s^2 pour vitesse 72km/h
DeltaAccelera=(277)/(Timer+1)( Timer) pour Timer=100ms=>0.027m/s^2 pour vitesse 18km/h

Pour ne pas avoir de décélération intempestive, le calcul de la décélération se fera seulement si l’erreur de mesure de la vitesse est plus faible que sa variation.
Etant donné que la propriété de la routine d’interruption extérieure n’est pas si facile à imposer par rapport à routine d’interruption timer sur Atmel, il n’y aura pas d’interruption timer.

Voici le code

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
//#include <TimerOne.h>


#define led13     13       //    
#define BPLeft    12       // 
#define Night     11       // 
#define BPRight   10       //
#define BPStop     9       //
 

LiquidCrystal lcd(8, 3, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables

 float Time=0; 
 float  TO=0; 
 float  temps=0; 
 float  Time1=0;               
            float kph=0;
            float kph1=0;
            float distance=0;

            float deceler=0;
            float erreur=0;
            bool stop=0;
            bool flagnight=0;
            bool attendre=0;
            
            byte   buffer1=0; 
            String readString;

// the setup function runs once when you press reset or power the board
void setup() {
 
pinMode(led13, OUTPUT);   //led carte arduino
pinMode (BPLeft, INPUT_PULLUP);
pinMode (Night, INPUT_PULLUP);
pinMode (BPRight, INPUT_PULLUP);
pinMode (BPStop, INPUT_PULLUP);

 lcd.begin(16, 2);                   //modifier pour un afficheur 20x4
 Serial.begin(9600);     //57600
 //SoftwareSerial mySerial(0, 1);   // RX, TX mais il n'y a pas trop le choix sur la nano


//  Timer1.initialize(10000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
//  Timer1.attachInterrupt(Routinetimer1);   // attaches le sous programme  Routinetimer1 as a timer overflow interrupt

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    

attachInterrupt(0, interrup2, RISING);   // il vaut mieux utiliser  KY003 tout ou rien   RISING, FALLING, CHANGE
TO = millis();                                   
}//fin setup

void interrup2() // la fonction appelée par l'interruption externe n°0
{//digitalWrite(led13,HIGH);
Time=(millis()-TO);  //mesure du temps     //micros()    millis()
TO=millis();

distance=(distance+(2.01/4));     //perimetre/4 aimants
kph1=kph;                            
if (Time>=500) {kph=0;deceler=0; } 
          else {kph=((2.010*1000*3.6)/(Time*4));   //kph=perimetre/time  avec 4 aimants   //
                erreur=(kph/(Time-2));                                                                             
                if ( (kph<(kph1-erreur)))  {deceler=(((kph-kph1)*277)/Time);} else {deceler=0;}   
                }              // en m/s^2  277=1000milliseconde/3.6      
if (deceler<-40) {deceler=-40; }
if (deceler>40) {deceler=40; }
//  digitalWrite(led13,LOW);   de la routine interr exterieur durée 0.7ms 
}



// Interruptions  tous les 100ms fait par le timer1***********************************

//void Routinetimer1()  {
//  temps++;  
//}//fin routineinterruption


///////////////////////////////////////////// Boucle correspondant à la fonction main 
void loop() {  
Time1=(millis()-TO);
if (Time1>=500) {kph=0;deceler=0;}   //remise à 0, lorsqu'il n'y a plus d'interruption
attendre=0;

  lcd.setCursor(0,0);  // (X,Y)                               
  lcd.print(kph,1);
  lcd.print("kph    "); 
  lcd.setCursor(9,0);  // (X,Y)                                
  lcd.print(distance/1000,2);
  lcd.print("km  ");
 
    lcd.setCursor(0,1);  // (X,Y)                            
    lcd.print(deceler,1);
    lcd.print("m/s"); 
    lcd.print((char)94);
    lcd.print("2 "); 

    if  (deceler<(-2)) {stop=1;lcd.setCursor(10,1);lcd.print("STOP");Serial.print(2); }  
    if  (deceler>0)    {stop=0,lcd.setCursor(10,1);lcd.print("    "); }
    if  (digitalRead(BPStop)==0) {stop=1;lcd.setCursor(10,1);lcd.print("STOP");Serial.print(2); }  
    if (digitalRead(BPLeft)==0)     {Serial.print(1); } 
    if (digitalRead(BPRight)==0 )   {Serial.print(3); } 
    if (digitalRead(BPRight)==0 && digitalRead(BPStop)==0   )    {distance=0;}   //remise à zero du compteur
    if  (digitalRead(BPStop)==0) {stop=1;lcd.setCursor(10,1);lcd.print("STOP");Serial.print(2); }
 if ((digitalRead(Night)==0) && (flagnight==0) && attendre==0 )      {flagnight=1;Serial.print(0);attendre=1; }   //eteindre la lumiere
 if ((digitalRead(Night)==0) && (flagnight==1) && attendre==0)      {flagnight=0;Serial.print(4);attendre=1;}   //allume lumiere
 if (flagnight==1)  {lcd.setCursor(15,0);lcd.print("N"); }      //lumiere
 if (flagnight==0)  {lcd.setCursor(15,0);lcd.print(" "); } 
 
/*Serial.print(Time,0);
Serial.print(";");
Serial.print(kph,1);
Serial.print(";");
Serial.print(deceler,1);
Serial.println(";");  */
delay(100);             
   
} // fin loop

la suite du post precedent

Voici le résultat de la mesure de la vitesse et la deceleration avec excel, du programme precedent

Pas envie de mettre 2 fils d’alimentation et 1 fil de communication entre le compteur et l’éclairage arrière entre le compteur et la partie éclairage arrière. Donc une commination série Bluetooth HC06 a été choisie.
Il sera possible d’indiquer sur le compteur rétroéclairer, l’état de la batterie arrière.

Le code de la récepteur et qui gère le clignotement

#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>

 
#define led12     12       // 
#define led11     11       // 
#define led10     10       // 

            byte  temps12=0;
            byte  temps11=0; 
            byte  temps10=0;
            byte c=0;
            bool flag12=0;
            bool flag11=0;
            bool flag10=0;
            bool flagnight=0;
            


void setup() {
pinMode(led12, OUTPUT);   //lumiere gauche, direction
pinMode(led11, OUTPUT);   //lumiere centrale, stop et eclairage
pinMode(led10, OUTPUT);   //lumiere gauche, direction


Serial.begin(9600);     
 //SoftwareSerial Serial(0, 1);   // RX, TX mais il n'y a pas trop le choix sur la nano

 Timer1.initialize(400000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
 Timer1.attachInterrupt(Routinetimer1);   // attaches le sous programme  Routinetimer1 as a timer overflow interrupt
                            
}//fin setup




// Interruptions  tous les 400ms fait par le timer1***********************************
void Routinetimer1()  {
  temps12++;
  temps11++;
  temps10++;
if (temps12<15 && flag12==1)  {if ( digitalRead(12)== 1 ) {digitalWrite(12,LOW);}  else {digitalWrite(12,HIGH);}}  //clignotement pendant 15=6s/0.4s  gauche
if (temps12>15 && flag12==1)  {flag12=0;digitalWrite(led12,LOW);} 

if (temps10<15 && flag10==1)  {if ( digitalRead(10)== 1 ) {digitalWrite(10,LOW);}  else {digitalWrite(10,HIGH);}}  //clignotement pendant 15=6s/0.4s droite
if (temps10>15 && flag10==1)  {flag10=0;digitalWrite(led10,LOW);} 

if (temps11<15 && flag11==1)  {if ( digitalRead(11)== 1 ) {digitalWrite(11,LOW);}  else {digitalWrite(11,HIGH);}}  //clignotement pendant 15=6s/0.4s stop
if (temps11>15 && flag11==1)  {flag11=0; 
                               if (flagnight==1) {digitalWrite(11,HIGH);}
                               if (flagnight==0) {digitalWrite(11,LOW);}
                              } 
}//fin routineinterruption



///////////////////////////////////////////// Boucle correspondant à la fonction main 
void loop() {  

while (Serial.available()) { c=Serial.read();//Serial.print(c,DEC);  
if (c==48)        {digitalWrite(led11,LOW);flagnight=0;}       //0 arret en continu
if (c==49)        {digitalWrite(led12,HIGH);digitalWrite(led10,LOW);flag12=1;flag10=0;temps12=0;}        //1  direction gauche +5s
if (c==50)        {digitalWrite(led11,HIGH);flag11=1;temps11=0;}                                         //2  feu stop
if (c==51)        {digitalWrite(led10,HIGH);digitalWrite(led12,LOW);flag10=1;flag12=0;temps10=0;}        //3  direction droite +5s
if (c==52)        {digitalWrite(led11,HIGH);flagnight=1;}                                                //4  allumage en continu
                            }

} // fin loop

A la place de faire de nombreux essais, sur le vélo pour vérifier le programme de la mesure vitesse et de la décélération, un Arduino génère les impulsions de l’aimant.

Voici le code du générateur de front qui fait varier la vitesse pour verifier le bon fonctionnement du programme

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <math.h>


 
#define led12     12       // 



LiquidCrystal lcd(9, 3, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables



unsigned    int   temps=0; 
unsigned    int tempsmono=0;
            float  tempsperiode=0;
            float   tempsdecelerat;
            float  tempsdec=0;
            float  a=0;
            float  b=0;
            float deceleration;
            bool flag;
            

// the setup function runs once when you press reset or power the board
void setup() {
 

pinMode(led12, OUTPUT);   //

 Serial.begin(9600);     //57600

  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(Routinetimer1);   // attaches le sous programme  Routinetimer1 as a timer overflow interrupt

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    


  a=0.144;                 //((400-40)/tdecel);   400  correspond à 4.5km/h , 40 à 45km/h utilsation 4 aimants    (4.5-45)/(2.5s*3.6)=4.5m/s^2
  b=40;                    //coefficient de le deceleration
    Serial.print(a,3);
    Serial.print(";");
    Serial.println(b,3) ;                               
}//fin setup



// Interruptions  tous les 1ms fait par le timer1***********************************
void Routinetimer1()  {
temps++;
tempsperiode++;
tempsmono++;

//*
if (tempsperiode<50)  {deceleration=2000;}     //creation profil acceleration et decele
if (tempsperiode>=50 &&  tempsperiode<2500)  {deceleration=-0.2*(tempsperiode-50)+400;        
                                              if (deceleration<40) {deceleration=40;}
                                              }    //acceleration
if (tempsperiode>=2500 &&  tempsperiode<3500)  {deceleration=40;}                                               
if (tempsperiode>=3500 &&  tempsperiode<6000)  {deceleration=a*(tempsperiode-3500)+b;}      //deceleration  

if (tempsperiode>=6000 && tempsperiode<7000 )  {deceleration=1000;}
if (tempsperiode>=7000)  {tempsperiode=0;} 
//*/
               
//Serial.print(tempsperiode,0) ; 
//Serial.print(";"); 
//Serial.println(deceleration,0) ;     
//deceleration=40;           //4 aimants,  400=>4.5km/h   40=>45km/h    valeur fixe de vitesse
   
if (temps>=deceleration  && flag==0) {digitalWrite(led12,HIGH);tempsmono=0;flag=1;temps=0; }  //monostable=10ms
if (flag==1 && tempsmono>=4)        {digitalWrite(led12,LOW);flag=0;} 




}//fin routineinterruption


///////////////////////////////////////////// Boucle correspondant à la fonction main 
void loop() {  
 


} // fin loop

Avec la batterie, du compteur, on pourrait faire aussi un éclairage blanc vers l’avant
Mais, cela sera une autre histoire….

Voici le programme du capteur accéléromètre MPU6050 mais avec un ESP32+OLED et un filtre passe bas du troisième ordre, pour un éclairage automatique arrière de vélo.

La méthode de test et des explications sont téléchargeables sur ce lien
https://www.fichier-pdf.fr/2020/09/17/esp32-mpu6050-arduino-filtre-passe-bas-1hz/

#include "Arduino.h"
#include "heltec.h"
#include "Wire.h"
//#include "BluetoothSerial.h"    //https://randomnerdtutorials.com/esp32-bluetooth-classic-arduino-ide/
                                  //


#define MPU_ADDRESS 0x68  // I2C address of the MPU-6050
int Te=100000  ; //periode d'echantillonnage micro seconde  =>0.1s

int16_t accX=0;
int16_t accY=0;
int16_t accZ=0;

 float accX_0=0;
 float accY_0=0;
 float accZ_0=0;

float accY_1 = 0;  // recurence n-1
float accY_2 = 0;  // recurence n-2
float accY_3 = 0;  // recurence n-3
float YaFout=0;
float YaF =0;
float YaF1 =0;
float YaF2 =0;
float YaF3 =0;

float  Y[127];     //12.7s



byte var=1;
byte i=0;   //incrementation for te curve
bool flagmesure=1;
bool flagenvoie=1;
bool flagstop=0;
bool flagfrein=0;
bool flagrear=0;
bool flaglight=0;
int16_t temps=0;
int16_t tensionbatt;




hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
uint32_t cp0_regs[18];

//******************************************************************
void IRAM_ATTR onTimer() {      //IRAM et DRAM   routine d'interruption toutes les TE 0.1s 
//digitalWrite(33,HIGH); 
 portENTER_CRITICAL_ISR(&timerMux);

xthal_set_cpenable(1);  xthal_save_cp0(cp0_regs);   // // Save FPU registers
if ( digitalRead(25)== 1 ) {digitalWrite(25,LOW);}  else {digitalWrite(25,HIGH);}    //led sur la carte heltec

flagmesure=1;
     
  xthal_restore_cp0(cp0_regs);  xthal_set_cpenable(0);  // and turn it back off
  portEXIT_CRITICAL_ISR(&timerMux);
  
//  digitalWrite(33,LOW);
}//fin routine interrupt







//*********************************
void setup(){
pinMode(0, INPUT_PULLUP);   //bouton prg carte heltec ESP32
pinMode(25, OUTPUT);   //led
//pinMode(13, OUTPUT);   //mesure temps instruction oscilloscope
pinMode(21, OUTPUT);   // lecture batterie lithium
 
 Serial.begin(115200);

 Wire.begin(4,15,400000);       //Wire.begin(I2C_SDA, I2C_SCL);       0x3C oled   et MPU6050  broche 4 et 15

  Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/);
  Heltec.display->flipScreenVertically();
  Heltec.display->setFont(ArialMT_Plain_24);           //10, 16, 24
  Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);

 
Wire.beginTransmission(MPU_ADDRESS);
 Wire.write(0x6B);  // PWR_MGMT_1 register
 Wire.write(0x00);     // set to zero (wakes up the MPU-6050)
 Wire.endTransmission();              // End the transmission

// Configure the acceleromter's sensitivity
Wire.beginTransmission(MPU_ADDRESS); // Start communication with MPU
Wire.write(0x1C);                    // Request the ACCEL_CONFIG register
Wire.write(0x00);                     // Apply the desired configuration to the register : ±2g=±20ms^-2     1g=16384
//Wire.write(0b00010000);             // Apply the desired configuration to the register : ±8g=±20ms^-2  1g=4096
Wire.endTransmission();              // End the transmission

// Configure low pass filter
Wire.beginTransmission(MPU_ADDRESS); // Start communication with MPU
Wire.write(0x1A);                    // Request the CONFIG register
//Wire.write(0x00);                    // Set Digital Low Pass Filter about ~260Hz   DLPF_CFG
Wire.write(0x06);                    // Set Digital Low Pass Filter about ~5Hz   DLPF_CFG
Wire.endTransmission();              // End the transmission

Wire.endTransmission(true);        //end the transmission

  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, Te, true);      
  timerAlarmEnable(timer);

}


//************************
void loop(){

//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}     //teste du temps de la boucle
if (digitalRead(0)==0)   {var++;delay(1500); }    //utilisation du bouton prg de la carte ESP32 heltec
if (var>=5)   {var=1;flagenvoie=1; }              //4 valeurs pour afficher les valeurs numeriques ou courbes ou pause ou envoie des données
if ( touchRead(13)<30 )   {flagfrein=1;} else {flagfrein=0;}    //touchRead(13)



if ((flagmesure==1 && (var==1 | var==2)))   {    //mesure tous les 0.1ms
  temps++;
Wire.beginTransmission(MPU_ADDRESS);
Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);           // End the transmission
Wire.requestFrom(MPU_ADDRESS,6);   // Request 16 bytes from the MPU-6050

// Wait until all the bytes are received
while(Wire.available() < 6);

accX  = (Wire.read() << 8 | Wire.read()); // reading registers: 0x3B et 0x3C     MSB et LSB
accY  = (Wire.read() << 8 | Wire.read()); // reading registers: 0x3D 
accZ  = (Wire.read() << 8 | Wire.read()); // reading registers: 0x3F 

accX_0=accX/1638.0;   //en m/s^2  pour initilisation à 2*gravitation
accY_0=accY/1638.0;
accZ_0=accZ/1638.0;

accY_3=accY_2;
accY_2=accY_1;  //recurence n-2
accY_1=accY_0;  //recurence n-1

YaF3=YaF2;
YaF2=YaF1;
YaF1=YaF;         //filtre numerique passe bas 3éme orde Frequence de coupure=1Hz
YaF=accY_0+3*accY_1+3*accY_2+3*accY_3+0.278059*YaF3-1.18289*YaF2+1.76*YaF1;   //filtre numerique passe bas  Frequence de coupure=1Hz
YaFout=YaF/66;        //55 en theorie

i++;
if (i>=127) {i=0;}
//Y[i]=accY_0;    //sans filtre
Y[i]=YaFout;      //avec filtre    

if (YaFout>=2)  {flagstop=1;temps=0;}     //declenchement lumiere peandant 5seconde clignotant
if (YaFout<=-1) {flagstop=0;flagrear=0;temps=0;flaglight=0;}   //si acceleration arret lumiere arriere
if (flagstop==1 && temps<=50) {flagrear=1;}
if (temps>50) {flagrear=0;flaglight=0;}   
if (flagrear==1) {       if (flaglight== 1) {flaglight=0;}  else {flaglight=1;}     }  
                      

flagmesure=0;
 }  //fin if flagmesure




switch (var) {
  case 1:        //affichage value
//mesure de la charge de la batterie lithium 18650
digitalWrite(21,LOW);                    //active le pont diviseur
tensionbatt=analogRead(37);              //avec pont diviseur 100K et 220k  4.2V=>1.3125  donc 1630 decimale
tensionbatt=(tensionbatt/39+4);      //(volt*10) 12 bits 4096    0.35V chute de tension dans le transitor     3.3*3.2/409.6=1/39
tensionbatt = map(tensionbatt, 30, 42, 0, 100);    //map(value, fromLow, fromHigh, toLow, toHigh)   % de la charge
                                                   //2.9V=>-8%   3V=>0%    4.2V=100%    

//affichage  acceleration Y et  poucentage de la charge de la batterie
Heltec.display->clear();       //affichage numerique acceleration si appuie sur PRG
//Heltec.display->drawString(0, 0, "IUT GEII Soissons"); 
//Heltec.display->drawString(0, 11, "X "+String(accX_0,1)+"   ");
Heltec.display->drawString(0, 0, "Y "+String(accY_0,1)+"  ");          //acceleration filtré à 5HZ par le MPU6050
Heltec.display->drawString(83,0, String(tensionbatt)+"%");           //en poucentage de la charge
Heltec.display->drawString(0, 25,"Yf"+String(YaFout,1)+"   ");
Heltec.display->drawString(83, 25,String(flagfrein));
Heltec.display->drawString(100, 25,String(flaglight));
Heltec.display->display(); 
 break;  //case 1

case 2:       //affichage curve 
 Heltec.display->clear(); 
 Heltec.display->drawHorizontalLine(0,32,127);  //(x,y,length)      //trace les axes
 for (byte im = 0; im <= 127; im++) {    // affichage de la courbe
 Heltec.display->setPixel(im,(32-Y[im]*3));   //mettre la courbe au millieu et augmenter l'echelle de 3
 Heltec.display->display();  }
 break;  //case 2

case 3:       //mode pause

break;  //case 3



case 4:       //send data monitor 
if (flagenvoie>=1) {
 for (byte im = 0; im <= 127; im++) {    // affichage de la courbe
 Serial.print( Y[im]);Serial.println(";"); }
 Serial.println("stop");
flagenvoie=0;     }
break;  //case 4
 

 }//end switch 
 
 
 }//end loop

Le programme complet avec l’odomètre arrive, mais le programme sera moins lisible, donc on préfère le décomposer en plusieurs programmes.

Voici le programme du compteur de velo avec un capteur a effet hall et un aimant avec un ESP32+OLED qui gere plus facilement la commande du feu arriére que le capteur d’acceleration.
Les explications sont ici
https://www.fichier-pdf.fr/2020/10/02/esp32-odometre-alarme-capacimetrertc-heure/
le MPU6050 a été gardé pour realiser une alarme.
Il reste plus qu’à faire un tracker avec un GPS et l’envoie d’une notification…..
Mais aussi realiser un boitier mécanique étanche, car pour l’instant ce n’est pas terrible


on va s’inpirer du boitier suivant

esp32_odometre_mpu6050_V3.ino (13.1 KB)

J’ai essayé votre programme sur un ESP32 TTGO avec un oled couleur d'aliexpress
Mais j’ai un souci car je n’arrive pas à afficher quelques choses sur le oled pourtant j’ai bien télécharge les bibliothèques comme expliqué ici, ainsi que d’inclure les chemins ou sont les bibliothèques et le driver.
cela compile bien

La variable i s’incrémente bien sur le moniteur série donc l’ESP32 fonctionne
Pourriez-vous m’eclairer, SVP, car je suis dans le brouillard

Je mets le minimum du programme pour faire fonctionner le oled ici

#include <TFT_eSPI.h>
#include <SPI.h>
//#include "WiFi.h"
#include <Wire.h>
//#include <Button2.h>
#include "esp_adc_cal.h"

#include <C:\Users\geii\Documents\Arduino\libraries\TFT_eSPI\User_Setups\Setup25_TTGO_T_Display.h>
//#include <C:\Users\geii\Documents\Arduino\libraries\TFT_eSPI\User_Setups\Setup24_ST7789.h>
//#include <User_Setup.h\Setup25_TTGO_T_Display.h> 


//#include <C:\Users\geii\Documents\Arduino\libraries\TFT_eSPI\User_Setups/Setup18_ST7789.h> 
//#include <C:\Users\geii\Documents\Arduino\libraries\TFT_eSPI\TFT_Drivers/ST7789_Defines.h>


//#define ST7789_DRIVER
//#define ILI9341_DRIVER

//#define CGRAM_OFFSET      // Library will add offsets required


//#define TFT_MOSI            19    //SDA
//#define TFT_SCLK            18    //SCL
//#define TFT_CS              5     //CS
//#define TFT_DC              16    //RS
//#define TFT_RST             23    //reset

//#define TFT_BL          4  // Display backlight control pin

#define TFT_BACKLIGHT_ON HIGH  // HIGH or LOW light OLED or not



//#define TFT_WIDTH  135
//#define TFT_HEIGHT 240
TFT_eSPI tft = TFT_eSPI (TFT_WIDTH, TFT_HEIGHT);  // édité



/*
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF

#define SMOOTH_FONT
*/


//#define SPI_FREQUENCY  27000000
//#define SPI_FREQUENCY    40000000   // Maximum for ILI9341


//#define SPI_READ_FREQUENCY  6000000 // 6 MHz is the maximum SPI read speed for the ST7789V

char buff[512];
int vref = 1100;
int i;

#define ADC_PIN 34

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

pinMode(2, OUTPUT);   //led
//pinMode(34, INPUT);   //led

tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);
tft.setTextColor(TFT_WHITE);
tft.setCursor(0, 0);
tft.setTextDatum(MC_DATUM);


if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h
         pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
         digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on. TFT_BACKLIGHT_ON has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h
    }
    

tft.setTextSize(1);
uint16_t v = analogRead(ADC_PIN);
float battery_voltage = ((float)v / 4095.0) * 2.0 * 3.3 * (vref / 1000.0);
String voltage = "Voltage :" + String(battery_voltage) + "V";
Serial.println(voltage);
tft.fillScreen(TFT_BLACK);
tft.setTextDatum(MC_DATUM);
tft.drawString(voltage, tft.width() / 2, tft.height() / 2 );
delay(1000);
tft.drawString("Press again to wake up", tft.width() / 2, tft.height() / 2 );
delay(1000);

     tft.fillRect(0,0,240,135, TFT_BLUE); // erase the screen display
     tft.setTextSize(3);   // (num colonne , num ligne)
     tft.setCursor(0, 35); // (x colonne , y ligne) - F6CZV
     tft.print("VFO"); // F6CZV
delay(1000);

}




void loop()
{
  i++;
  if ( digitalRead(2)== 1 ) {digitalWrite(2,LOW);}  else {digitalWrite(2,HIGH);}
  Serial.println(i);
//tft.setRotation(0);

tft.fillScreen(TFT_RED);
delay(1000);
tft.fillScreen(TFT_BLUE);
//espDelay(1000);
delay(1000);
tft.fillScreen(TFT_GREEN);
delay(1000);
}

il y a une instrumentation de velo sur ce forum ici, j’ai envoyé un mail mais sans reponse
http://cyclurba.fr/forum/570020/dn-codage-protocole-sn-rie-bbs0x.html?from=571&discussionID=18542&messageID=638753&rubriqueID=102&pageprec

Ca peut venir du fait que le constructeur

TFT_eSPI tft = TFT_eSPI(135, 240); // Invoke custom library

est avant les define des pins du SPI.

J'utilise ce même module TTGO et ça fonctionne bien.
Il faut sélectionner

#define ILI9341_DRIVER

dans le fichier User_Setup.h de la bibliothèque TFT_eSPI. Les autres lignes doivent commentées :

// Only define one driver, the other ones must be commented out
#define ILI9341_DRIVER
//#define ST7735_DRIVER      // Define additional parameters below for this display
//#define ILI9163_DRIVER     // Define additional parameters below for this display
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
//#define HX8357D_DRIVER
//#define ILI9481_DRIVER
//#define ILI9486_DRIVER
//#define ILI9488_DRIVER     // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
//#define ST7789_DRIVER      // Full configuration option, define additional parameters below for this display
//#define ST7789_2_DRIVER    // Minimal configuration option, define additional parameters below for this display
//#define R61581_DRIVER
//#define RM68140_DRIVER
//#define ST7796_DRIVER
//#define SSD1963_480_DRIVER    // Untested
//#define SSD1963_800_DRIVER    // Untested
//#define SSD1963_800ALT_DRIVER // Untested
1 Like

A partir d’un d’éclairage arrière chinois à 5€, un Arduino pro mini a été programmé pour optimiser les performances de l’éclairage de la LED en mode flash et d’optimiser l’autonomie.
En effet, un capteur de luminosité permet de faire d’augmenter l’éclairage de la led lorsque la luminosité extérieure est importante pour être bien vu de jour, mais aussi par temps de brouillard.
C’est éclairage est devenu le plus performant de ce qui existe sur la marché.

Mais, il est possible de paramétrer l’éclairage par Bluetooth comme on peut le voir sur la photo suivante

En PDF, la méthodologie et le programme pour optimiser l’éclairage et les choix effectués ainsi que le programme…..

D’autres prototypes devraient voir le jour avec amélioration et la minimisation du volume de l’électronique et du boitier.

1 Like

Suite au PDF précèdent,

Pour minimiser le câblage et éviter le problème du téléchargement d’un nouveau sketch de devoir déconnecter le HC06 à cause qu’il transmet tout le temps

J’ai voulu remplacé le HC06 par le BLE NAN0
Mais, je suis très déçu du BLE keywish qui ne se gère pas du tout comme un HC06
le manuel est tres bien fait
[keywish-nano-plus/Ble-Nano operation manual V.1.6.pdf at master · keywish/keywish-nano-plus · GitHub](http://"https://github.com/keywish/keywish-nano-plus/blob/master/BLE-Nano/Ble-Nano operation manual V.1.6.pdf")

Par contre, il faut utiliser leurs applications
BLETestToosV1.0.apk

Mais qui est en mandarin, j’arrive quand même à commander l’éclairage et lire les valeurs du bluetooth….mais plus possible d’utiliser l’application bluetoothelectronics

J’ai essayé de modifier le paramétrage avec la commande AT, le BLE NANO mais sans succès
j'ai envoyé un mail au support keywish, sans reponse depuis 1 semaine

La consommation moyenne du BLE nano est de 6mA avec un envoie 33 caracteres tous les 0.1s

On a modifié le programme de test du courant pour voir une commande continu et de modifier la periode lors de test de nouvelle LED

exemple, on a rajouté 3 led de petite puissance pour etre vu sur le coté
L’éclairage sur le côté ne demande pas d’être vu à 150m, sachant que lorsque l’on a un éclairage de de 10Watt à l’avant son flux est visible de côté ainsi que la réflexion de l’éclairage sur le sol
Pour être vu de coté à 6m avec 0.7 lux sur une largeur de 2m, il faut un angle d’environ de 6°
Avec 1 lumen ou 14 candela comme le démontre les équations suivantes

Par conséquent, de petite leds faibles puissance (50mA). seront suffisantes de diametre 5mm.
(3 led seront utilisé, 2 sur le coté et une pour l’arriere).

2 solutions pour faire la commande des 3 leds (3*50mA).

  • soit utiliser une résistance de limitation de courant qui aura des pertes mais sera une commande très fiable.
  • soit utiliser un hacheur qui minimisera les pertes mais si la régulation de courant n’est pas correcte alors il y a un risque de destruction des led et du transistor de commande. Pour ce courant pas la peine de prendre un transistor super-dimensionné avec un boitier TO251 mais un petit boitier SOT23 suffira
    L’inductance sera aussi bien plus petite devant supporte seulement un courant 150mA.

exemple de choix de led __*C503B-RAS *__6° 50mA 15 candela.
en mode eclairement continu, on peut mesurer les lux à 50cm sur un mur

Toujours à 50cm l’éclairage en fonction du courant est bien correct autour de 50mA avec un coefficient de performance de 3200lux/A
Puis, au-delà de 0.1A, les performances de l’éclairage se dégradent fortement.

Voici la commande des 3 led avec hacheur en boucle ouverte et de la led de puissance en simulation

le programme de test des leds en mode continu et pediodique en piece jointe

MPU6050_nano_blue_v1.ino (6 KB)