////////////////////////////////////////////////////////////////////
class CStateMachineRotary //déclaration de la classe
{
public:            //membres accessibles depuis l'extérieur de la classe, il s'agit de l'interface d'interaction de la classe
  CStateMachineRotary(int brocheAinit, int brocheBinit); //Constructeur avec paramètres pour régler les 2 broches Arduino utilisées
  void clock();    //méthode pour cadencer la machine à état (faire 1 coup d'horloge)
  int getPosition()//méthode accesseur pour accéder à l'attribut privé position
	{ return position;} //on parle de méthode inlinée car l'implémentation est faite dans la déclaration de la classe
  void setPosition(int newposition)
  { this->position=newposition;}

private:        //membres privés pour réaliser l'encapsulation: ces attributs sont inacessibles directement depuis l'extérieur de la classe
  byte etat;    //numéro de l'état actif
  byte entreeA; //valeur lue sur l'entrée A
  byte entreeB; //valeur lue sur l'entrée B
  int brocheA;  //numéro de broche Arduino utilisée pour l'entrée A
  int brocheB;  //numéro de broche Arduino utilisée pour l'entrée B
  virtual void frontDetecte(){}; //methode abstraite, sera implémentée dans la classe fille
  virtual void actionSurTousLesEtats(){}; //methode abstraite, sera implémentée dans la classe fille
protected:      //ces membres peuvent être accédés dans les classes filles
  int position; //position angulaire mesurée    
};  //ne pas oublier le ;
////////////////////////////////////////////////////////////////////
//Implémentation de la méthode constructeur
CStateMachineRotary::CStateMachineRotary(int brocheAinit, int brocheBinit){
// à compléter
  etat=0;    //numéro de l'état actif
  brocheA=brocheAinit;  //numéro de broche Arduino utilisée pour l'entrée A
  brocheB=brocheBinit;  //numéro de broche Arduino utilisée pour l'entrée B
  //broches en entrées
  pinMode(brocheA,INPUT_PULLUP);
  pinMode(brocheB,INPUT_PULLUP);
  entreeA=digitalRead(brocheA); //valeur lue sur l'entrée A
  entreeB=digitalRead(brocheB); //valeur lue sur l'entrée B
  position=0; 
}
//Implémentation de la méthode clock
void CStateMachineRotary::clock(){
// à compléter


//lecture et mémorisation des entrées
 entreeA=digitalRead(brocheA); //valeur lue sur l'entrée A
 entreeB=digitalRead(brocheB); //valeur lue sur l'entrée B
  #ifdef DEBUG
    Serial.print("A:");
    Serial.print(entreeA,HEX);
    Serial.print(" B:");
    Serial.print(entreeB,HEX);
    Serial.print(" e:");
    Serial.print(etat,HEX);
    Serial.print(" p:");
    Serial.print(position,DEC);
    
    Serial.println();

#endif

//actions sur transition
 if ( (etat==0) && (entreeA==1) )
    {
      position++;
      frontDetecte();
      }
 if ( (etat==1) && (entreeA==0) )
    {
      position--;
    frontDetecte();
      }

//évolution de l'état
 switch (etat){
	case 0: if (entreeA==1) etat=1;
          else if (entreeB==1) etat=3;
          //else etat=0;
      		break;
	case 1: if (entreeB==1) etat=2;
          else if (entreeA==0) etat=0;
          //else etat=0;
      		break;
	case 2: if (entreeA==0) etat=3;
          else if (entreeB==0) etat=1;
          //else etat=0;
      		break;
	case 3: if (entreeB==0) etat=0;
          else if (entreeA==1) etat=2;
          //else etat=0;
      		break;
 
  default:
  		break;
    
        }
//actions sur état
actionSurTousLesEtats();
}


////////////////////////////////////////////////////////////////////
class CStateMachineRotaryWithSpeed: public CStateMachineRotary
{
public:
  CStateMachineRotaryWithSpeed(int brocheAinit, int brocheBinit);
  float getSpeed(); //méthode accesseur pour accéder à l'attribut privé speed
 
protected:
  float speed;  //vitesse mesurée
private:
 
  unsigned long lastPulseTime; //horodatage du dernier front
  void frontDetecte(); //implémentation pour effectuer des traitements lorsqu'un front est detecté
};
////////////////////////////////////////////////////////////////////
//Implémentation du constructeur de la classe fille, exécutée après avoir exécuté le constructeur de la classe mère
CStateMachineRotaryWithSpeed::CStateMachineRotaryWithSpeed(int brocheAinit, int brocheBinit): CStateMachineRotary(brocheAinit, brocheBinit){
 lastPulseTime=micros(); //initialisation de la date initiale pour l'horodatage des fronts
 speed=0; //initialise vitesse nulle au démarrage. Attention, en simu, l'encodeur génère des fronts parasites
}  
////////////////////////////////////////////////////////////////////
//implémentation de la méthode frontDetecte() qui n'était pas implémentée dans la classe mère
void CStateMachineRotaryWithSpeed::frontDetecte()
{
  // à compléter

unsigned long newPulseTime=micros();
speed=1000000. /  (newPulseTime-lastPulseTime);
lastPulseTime=newPulseTime;
 }
//implémentation de la mesure de vitesse
float CStateMachineRotaryWithSpeed::getSpeed(){
  //à faire: calculer la vitesse et l'affecter à l'attribut speed

unsigned long newTime=micros();
if ((newTime-lastPulseTime)>4000000)
    speed=0;
  return speed;
}

//ajoute une broche pour la remise à zero de position dans n'importe quel état
class CStateMachineRotaryWithSpeedAndReset: public CStateMachineRotaryWithSpeed
{
public:
  CStateMachineRotaryWithSpeedAndReset(int brocheAinit, int brocheBinit,int brocheRinit); //Ce constructeur dispose d'un paramètre supplémentaire pour configurer la broche utilisée pour le bouton
private:
  int brocheR; //un nouvel attribut pour stocker le numéro de la broche servant au reset
  void actionSurTousLesEtats(); //implémentation de la méthode permettant de gérer l'action "appui sur le bouton reset" indépendamment de l'état courant de la machine à état
};
////////////////////////////////////////////////////////////////////
CStateMachineRotaryWithSpeedAndReset::CStateMachineRotaryWithSpeedAndReset(int brocheAinit, int brocheBinit,int brocheRinit): CStateMachineRotaryWithSpeed(brocheAinit, brocheBinit){
brocheR=brocheRinit;
pinMode(brocheR,INPUT_PULLUP);
}
////////////////////////////////////////////////////////////////////
void CStateMachineRotaryWithSpeedAndReset::actionSurTousLesEtats(){
if (digitalRead(brocheR)==0){  //ici on dévie légèrement du modèle de la machine à état car il faudrait idéalement avoir lu cette entrée en même temps que les autres à l'étape lecture des entrées
  position=0; 
  speed=0;
  //Serial.println("raz position");
  }
}


CStateMachineRotary encoder1(A4,A5);
CStateMachineRotaryWithSpeedAndReset encoder2(A0,A1,A2);

void setup() {
Serial.begin(115200);
//configurer et initialiser ce qui doit l'être
 
}
// #define DEBUG
void loop() {
 
 encoder1.clock();
encoder2.clock();

//affichage de la valeur mesurée 10 fois par seconde
unsigned int periodiciteTache1=100;
static unsigned long timerTache1 = millis();
if (millis() - timerTache1 >= periodiciteTache1) {
    timerTache1 += periodiciteTache1;
    Serial.print(encoder1.getPosition());
    Serial.print(" ");
    Serial.print(encoder2.getPosition());
    Serial.print(" ");
    Serial.println(encoder2.getSpeed());
}
 

 unsigned int periodiciteTache2=10000;
static unsigned long timerTache2 = millis();
if (millis() - timerTache2 >= periodiciteTache2) {
      timerTache2 += periodiciteTache2;
encoder2.setPosition( encoder2.getPosition()+4);
}
}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
D0D1D2D3D4D5D6D7GNDLOGIC
logic1:D0
logic1:D1
logic1:D2
logic1:D3
logic1:D4
logic1:D5
logic1:D6
logic1:D7
logic1:GND
leda:A
leda:C
swa:1
swa:2
swa:3
ledb:A
ledb:C
swb:1
swb:2
swb:3
encoder1:CLK
encoder1:DT
encoder1:SW
encoder1:VCC
encoder1:GND