// 23/12/2022 18:30
// Ajout de tempos anti-rebonds (t[3] et t[4] : 20 ms)
//******** Début code Ecran déclarations ********
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
//******** Fin code Ecran déclarations ********
//******** Début code Encodeur déclarations ********
#define ENCODER_CLK 2
#define ENCODER_DT 3
volatile unsigned int counter = 0;
//******** Fin code Encodeur déclarations ********
//******** Déclarations variables liées au débitmètre ********
//float volume=0;
unsigned int volume = 0; // volume en cours
float volume_par_impulsion = 2.25; // pour 2,25 mL
unsigned int volume_bouteille = 33; // pour 33 cL
//******** Fin déclarations variables liées au débitmètre ********
//******** Début code Apimou déclarations ********
//Pour visualiser les numeros des etapes actives en cours de cycle avec le moniteur serie
#define Visu_Etapes
//Entrees
bool I[3];
const byte bp_start = 5;
const byte debitmetre = 6;
const byte clic = 4;
//Sorties
bool O[3];
const byte Ev_gaz_in = 10;
const byte Ev_beer_in = 11;
const byte Ev_gaz_out = 12;
//Bits internes
bool B[2];
//Etapes
//(variables)
bool X[11];
#ifdef Visu_Etapes
//(memos des variables X au cycle precedent)
bool X_prev[11];
#endif
//(numeros)
const byte numero_etape[] = {0,1,2,3,4,10,11,12,13,14,15};
//Transitions
//(variables correspondant aux transitions franchissables)
bool T[11];
//Temporisations
typedef struct{
bool IN; //INput
unsigned long PT; //Preset Time
unsigned long ET; // Elapsed Time
unsigned long ST; // Start Time
bool Q; //Sortie
}tempo;
tempo t[5];
//Compteurs
typedef struct{
bool CU; //Count Up
bool pre; //Valeur precedente
bool R; //Reset
int PV; //Preset Value
int CV; // Current Value
bool Q; //Sortie
}compteur;
compteur ct[1];
//******** Fin code Apimou déclarations ********
void setup() {
//******** Début code Ecran setup ********
// Initialize LCD
lcd.init();
lcd.backlight();
//******** Fin code Ecran setup ********
//******** Début code Encodeur setup ********
// Initialize encoder pins
pinMode(ENCODER_CLK, INPUT);
pinMode(ENCODER_DT, INPUT);
attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), readEncoder, FALLING);
//******** Fin code Encodeur setup ********
//******** Début code Apimou setup ********
#if defined (Visu_Etapes)
Serial.begin(115200);
#endif
// Configuration des broches (entrees)
pinMode(bp_start, INPUT_PULLUP);
pinMode(debitmetre, INPUT_PULLUP);
pinMode(clic, INPUT_PULLUP);
// Configuration des broches (sorties)
pinMode(Ev_gaz_in, OUTPUT);
pinMode(Ev_beer_in, OUTPUT);
pinMode(Ev_gaz_out, OUTPUT);
// Initialisation du grafcet
memset(X, false, sizeof(X));
X[0] = true;
X[5] = true;
#ifdef Visu_Etapes
memset(X_prev, false, sizeof(X_prev));
#endif
// Initialisation des tempos
t[0].PT = 1000;
t[1].PT = 1000;
t[2].PT = 1000;
t[3].PT = 20;
t[4].PT = 20;
for(byte i=0;i<5;i++){
t[i].ST = 0;
}
// Initialisation du compteur
//******** Modification du code Apimou ********
ct[0].PV = volume_bouteille *10 / volume_par_impulsion;
//******** Fin de modification du code Apimou ********
ct[0].pre = false;
ct[0].CV = 0;
ct[0].Q = false;
//******** Fin code Apimou setup ********
}
//******** Fonction appelée par l'interruption de l'Encodeur ********
void readEncoder() {
int dtValue = digitalRead(ENCODER_DT);
if (dtValue == HIGH) {
if(X[9]||X[10]){
counter++;
}
else{
counter +=100;
} // Clockwise
}
if (dtValue == LOW) {
if(X[9]||X[10]){
counter--;
}
else{
counter -=100;
} // Counterclockwise
}
}
//******** Fin de fonction appelée par l'interruption de l'Encodeur ********
//******** Fonction retournant la valeur du compteur de l'Encodeur ********
int getCounter() {
int result;
noInterrupts();
result = counter;
interrupts();
return result;
}
//******** Fin de fonction retournant la valeur du compteur de l'Encodeur ********
//******** Fonction remettant à une valeur donnée la valeur du compteur de l'Encodeur ********
void resetCounter(int valeur_initiale) {
noInterrupts();
counter = valeur_initiale;
interrupts();
}
//******** Fin de fonction remettant à 0 la valeur du compteur de l'Encodeur ********
void loop() {
//******** Déclaration de variables permettant de savoir s'il y a lieu de modifier l'affichage
static bool increment_compteur = true;
static bool changt_parametre = true;
//******** Fin de déclaration de variables permettant de savoir s'il y a lieu de modifier l'affichage
//******** Déclaration de variables permettant de formatter le texte
static char buffer [17];// a few bytes larger than your LCD line
//******** Fin de déclaration de variables permettant de formatter le texte
//******** Début code Apimou loop ********
// Acquisition des entrees
I[0] = !digitalRead(bp_start);
I[1] = !digitalRead(debitmetre);
I[2] = !digitalRead(clic);
//Traitement des bits internes
B[0] = I[2] && !(B[1]);
B[1] = I[2];
//Traitement des tempos
t[0].IN = X[1];
t[1].IN = X[2];
t[2].IN = X[3];
t[3].IN = X[6] || X[8] || X[10];
t[4].IN = X[7] || X[9];
for(byte i=0;i<5;i++){
if(t[i].IN){
if(t[i].ST == 0){
t[i].ST = millis();
}
t[i].ET = millis() - t[i].ST;
if(t[i].ET > t[i].PT){
t[i].Q = true;
}
}
else{
t[i].ST = 0;
t[i].ET = 0;
t[i].Q = false;
}
}
//Traitement du compteur
ct[0].CU = I[1];
ct[0].R = X[0];
if(ct[0].R){
ct[0].CV = 0;
ct[0].Q = false;
}
else{
if(ct[0].pre == false && ct[0].CU == true){
ct[0].CV++;
//******** Ajout au code Apimou ********
increment_compteur=true;
//******** Fin d'ajout au code Apimou ********
}
}
if(ct[0].CV >= ct[0].PV){
ct[0].Q = true;
}
ct[0].pre = ct[0].CU;
//Traitement des transitions
T[0] = X[0] && (I[0]);
T[1] = X[1] && (t[0].Q);
T[2] = X[2] && (t[1].Q);
T[3] = X[3] && (t[2].Q);
T[4] = X[4] && (ct[0].Q);
T[5] = X[5] && (B[0] && X[0]);
T[6] = X[6] && (B[0] && X[0] && t[3].Q);
T[7] = X[7] && (B[0] && X[0] && t[4].Q);
T[8] = X[8] && (B[0] && X[0] && t[3].Q);
T[9] = X[9] && (B[0] && X[0] && t[4].Q);
T[10] = X[10] && (B[0] && X[0] && t[3].Q);
//Traitement des etapes
X[0] = X[0] && !T[0] || T[4];
X[1] = X[1] && !T[1] || T[0];
X[2] = X[2] && !T[2] || T[1];
X[3] = X[3] && !T[3] || T[2];
X[4] = X[4] && !T[4] || T[3];
X[5] = X[5] && !T[5] || T[10];
X[6] = X[6] && !T[6] || T[5];
X[7] = X[7] && !T[7] || T[6];
X[8] = X[8] && !T[8] || T[7];
X[9] = X[9] && !T[9] || T[8];
X[10] = X[10] && !T[10] || T[9];
//Visualisation des etapes actives
#ifdef Visu_Etapes
if(memcmp(X, X_prev, sizeof(X))!=0){
Serial.print("Etape(s) active(s) : ");
bool first = true;
for(byte i=0;i<11;i++){
if(X[i]){
if(first == false){
Serial.print(", ");
}
else{
first = false;
}
Serial.print(numero_etape[i]);
}
X_prev[i] = X[i];
}
Serial.println();
}
#endif
//Traitement des sorties
O[0] = X[1] || X[3];
O[1] = X[4];
O[2] = X[2];
// Ecriture des sorties
digitalWrite(Ev_gaz_in, O[0]);
digitalWrite(Ev_beer_in, O[1]);
digitalWrite(Ev_gaz_out, O[2]);
//******** Fin code Apimou loop ********
//******** Début code Afficheur et Encodeur loop ********
if(T[5]||T[6]||T[7]||T[8]||T[9]||T[10]){
changt_parametre=true;
}
//Si l'étape n°10 est active on affiche le volume en cours
if(X[5]){
// On change l'affichage si on a opéré un changement de paramètre visualisé
if(changt_parametre==true){
lcd.setCursor(0, 0);
lcd.print("V. delivre (mL):");// 16 caractères
volume = ct[0].CV * volume_par_impulsion;
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", volume);
lcd.print (buffer);
changt_parametre=false;
}
// On change l'affichage de la deuxième ligne si le débitmètre a envoyé une nouvelle impulsion
// ou si on redémarre le cycle en franchissant T[0]
else if(T[0] == true){
volume = ct[0].CV * volume_par_impulsion;
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", volume);
lcd.print (buffer);
}
else if(increment_compteur == true){
volume = ct[0].CV * volume_par_impulsion;
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", volume);
lcd.print (buffer);
increment_compteur=false;
}
}
//Si l'étape n°11 est active (ainsi que la n°0) on affiche la première tempo
if(X[6] && X[0]){
// On change l'affichage si on a opéré un changement de paramètre visualisé
if(changt_parametre==true){
lcd.setCursor(0, 0);
lcd.print(" Tempo t0 (ms) :");// 16 caractères
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", t[0].PT);
lcd.print (buffer); // display buffer on line
changt_parametre=false;
//On réinitialise le compteur géré par l'interruption avec la présélection de la tempo
resetCounter(t[0].PT);
}
// On change l'affichage de la deuxième ligne si la valeur de présélection a changé
if(getCounter() != t[0].PT){
t[0].PT = getCounter();
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", t[0].PT);
lcd.print (buffer);
}
}
//Si l'étape n°12 est active (ainsi que la n°0) on affiche la seconde tempo
if(X[7] && X[0]){
// On change l'affichage si on a opéré un changement de paramètre visualisé
if(changt_parametre==true){
lcd.setCursor(0, 0);
lcd.print(" Tempo t1 (ms) :");// 16 caractères
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", t[1].PT);
lcd.print (buffer);
changt_parametre=false;
//On réinitialise le compteur géré par l'interruption avec la présélection de la tempo
resetCounter(t[1].PT);
}
// On change l'affichage de la deuxième ligne si la valeur de présélection a changé
if(getCounter() != t[1].PT){
t[1].PT = getCounter();
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", t[1].PT);
lcd.print (buffer);
}
}
//Si l'étape n°13 est active (ainsi que la n°0) on affiche la troisième tempo
if(X[8] && X[0]){
// On change l'affichage si on a opéré un changement de paramètre visualisé
if(changt_parametre==true){
lcd.setCursor(0, 0);
lcd.print(" Tempo t2 (ms) :");// 16 caractères
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", t[2].PT);
lcd.print (buffer);
changt_parametre=false;
//On réinitialise le compteur géré par l'interruption avec la présélection de la tempo
resetCounter(t[2].PT);
}
// On change l'affichage de la deuxième ligne si la valeur de présélection a changé
if(getCounter() != t[2].PT){
t[2].PT = getCounter();
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", t[2].PT);
lcd.print (buffer);
}
}
//Si l'étape n°14 est active (ainsi que la n°0) on affiche la consigne de remplissage
if(X[9] && X[0]){
// On change l'affichage si on a opéré un changement de paramètre visualisé
if(changt_parametre==true){
lcd.setCursor(0, 0);
lcd.print("Vol. bout. (cL):");// 16 caractères
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", volume_bouteille);
lcd.print (buffer);
changt_parametre=false;
//On réinitialise le compteur géré par l'interruption avec la présélection du compteur
resetCounter(volume_bouteille);
}
// On change l'affichage de la deuxième ligne si la valeur de présélection a changé
if(getCounter() != volume_bouteille){
volume_bouteille = getCounter();
ct[0].PV = volume_bouteille *10 / volume_par_impulsion;
lcd.setCursor(0, 1);
sprintf (buffer, "%10u", volume_bouteille);
lcd.print (buffer);
}
}
//Si l'étape n°15 est active (ainsi que la n°0) on affiche le volume pour une impulsion de débitmètre
if(X[10] && X[0]){
// On change l'affichage si on a opéré un changement de paramètre visualisé
if(changt_parametre==true){
lcd.setCursor(0, 0);
lcd.print("Vol/impuls.(mL):");// 16 caractères
lcd.setCursor(0, 1);
dtostrf(volume_par_impulsion,10,2,buffer);
lcd.print (buffer);
changt_parametre=false;
//On réinitialise le compteur géré par l'interruption avec le volume par impulsion
resetCounter(volume_par_impulsion*100.00);
}
// On change l'affichage de la deuxième ligne si la valeur de présélection a changé
if(getCounter() != int(volume_par_impulsion*100.00)){
volume_par_impulsion = getCounter()/100.00;
ct[0].PV = volume_bouteille *10 / volume_par_impulsion;
lcd.setCursor(0, 1);
dtostrf(volume_par_impulsion,10,2,buffer);
lcd.print (buffer);
}
}
//******** Fin code Afficheur et Encodeur loop ********
}