// Blocage Servo Moteur
// https://forum.arduino.cc/t/blocage-servo-moteur/1305482
#define SIMULATION
typedef uint16_t short_time_t;
typedef uint32_t time_t;
#include "Servo.h" //inclure la bibliothèque Servomoteur
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal_I2C.h> // inclure la bibliothèque I2C
LiquidCrystal_I2C lcd(0x27, 16, 2); // adresse i2c , nombre de colonnes, nombre de lignes de l'écran
const byte PinModeManuel = 4; //Bleu
const byte PinVanne[] = {12, 7, 8};
const byte pinServo[] = {11, 6, 9};
const uint8_t pinCourantVanne[] = {A0, A1, A2};
enum : byte {AUTO, MANUEL, START} mode = START;
enum : byte {NORD, EST, OUEST, PISCINE};
enum : byte {DEMARRE, REGULE} modeAuto = DEMARRE;
#define VANNE_OUVERTE 0
#define VANNE_FERMEE 90
#define COURANT_VANNE_MIN 100 // Seuil de courant consommé par une vanne en mouvement
#define COURANT_VANNE_MAX 750 // Courant indiquant une vanne grippée
char *NomVanne[] = {"NORD", "EST", "OUEST"};
const int8_t pos[] = {15, 6, 15};
#define DIX_MINUTES 10000UL
#define TEMPS_MOUVEMENT_SERVO 2000UL
#define MARGE_DE_SECURITE 2000UL
#define DECALAGE_VANNES 1000UL
#define QUARANTES_MINUTES 40000UL
#define DELAI_DEMARRAGE_SERVO 200UL
/* Broche du bus 1-Wire */
const byte BROCHE_ONEWIRE = 3;
/* Adresses des capteurs de température */
const byte SENSOR_PISCINE_ADDRESS[] = { 0x28, 0xBA, 0xC5, 0x99, 0x5F, 0x20, 0x01, 0xEF };
const byte SENSOR_EST_ADDRESS[] = { 0x28, 0xF6, 0xD2, 0x13, 0x37, 0x20, 0x01, 0x88 };
const byte SENSOR_OUEST_ADDRESS[] = { 0x28, 0x64, 0x09, 0x80, 0x5C, 0x20, 0x01, 0x74 };
const byte SENSOR_NORD_ADDRESS[] = { 0x28, 0x58, 0xE0, 0xDB, 0x5F, 0x20, 0x01, 0x65 };
/* Création de l'objet OneWire pour manipuler le bus 1-Wire */
OneWire ds(BROCHE_ONEWIRE);
DallasTemperature sensors(&ds);
#define NOMBRE_VANNES 3
Servo servo[NOMBRE_VANNES]; // création des objets "servo"
float temperature[4];
time_t tempsActuel;
time_t tempsReference[NOMBRE_VANNES];
int positionCible[NOMBRE_VANNES];
enum : uint8_t {OUVERTURE_VANNE, PAUSE_10_MN, REGULATION_TEMPERATURE_40_MN_MAX, FERMETURE_VANNE, ATTENTE_NOUVEAU_CYCLE} statutVanne[NOMBRE_VANNES];
// Définiton de symboles spécifiques pour l'afficheur réduit 16x2
enum : uint8_t {charAuto = 5, charManuel, charDegC};
const uint8_t DegC[8] = { // °C
0b11000,
0b11000,
0b00000,
0b00110,
0b01000,
0b01000,
0b00110,
0b00000
};
const uint8_t Auto[8] = {
0b00110,
0b01001,
0b01111,
0b01001,
0b00000,
0b01001,
0b01001,
0b00110
};
const uint8_t Manuel[8] = {
0b11011,
0b10101,
0b10001,
0b10001,
0b00110,
0b01001,
0b01111,
0b01001
};
const uint8_t decimal[][8] = { // chiffres 0 à 9 de petite taille précédés d'un point décimal
{ // 0
0b00110,
0b01001,
0b01011,
0b01101,
0b01001,
0b00110,
0b10000,
0b00000
},
{ // 1
0b00010,
0b00110,
0b00010,
0b00010,
0b00010,
0b00111,
0b10000,
0b00000
},
{ // 2
0b00110,
0b01001,
0b00001,
0b00010,
0b00100,
0b01111,
0b10000,
0b00000
},
{ // 3
0b01111,
0b00001,
0b00010,
0b00001,
0b01001,
0b00110,
0b10000,
0b00000
},
{ // 4
0b00010,
0b00110,
0b01010,
0b01111,
0b00010,
0b00010,
0b10000,
0b00000
},
{ // 5
0b01111,
0b01000,
0b01110,
0b00001,
0b01001,
0b00110,
0b10000,
0b00000
},
{ // 6
0b00110,
0b01000,
0b01110,
0b01001,
0b01001,
0b00110,
0b10000,
0b00000
},
{ // 7
0b01111,
0b01001,
0b00001,
0b00010,
0b00100,
0b00100,
0b10000,
0b00000
},
{ // 8
0b00110,
0b01001,
0b00110,
0b01001,
0b01001,
0b00110,
0b10000,
0b00000
},
{ // 9
0b00110,
0b01001,
0b01001,
0b00111,
0b00001,
0b00110,
0b10000,
0b00000
}
};
void lcdPrint(float temperature, byte colonne, byte ligne) {
static int8_t charNum = 0;
// Pour des questions de place sur l'afficheur, seules les températures de -9.9 à 99.9°C sont affichées
if (-10.0 < temperature && temperature < 100.0) {
int16_t temp = int(abs(temperature * 10.0) + .5); // temp contient l'arrondi de temperature * 10
lcd.createChar(charNum, decimal[temp % 10]); // choisir le chiffre de la partie décimale
String str = String((temperature > -.05) ? temp / 10 : -temp / 10); // afficher la partie entière avec son signe
lcd.setCursor(colonne, ligne);
if (str.length() < 2) { // et ajouter un espace si nécessaire
lcd.print(' ');
}
lcd.print(str);
lcd.print((char)charNum);
}
else { // Affichage lorsque la température est hors limites
lcd.setCursor(colonne, ligne);
lcd.print(F("***"));
}
charNum = (charNum + 1) % 4;
}
void afficheTemperatures(const float *temperature) {
Serial.print(F("Temperatures: Piscine:"));
Serial.print(temperature[PISCINE], 4);
Serial.write('°'); // Caractère degré
Serial.print(F("C, Est: "));
Serial.print(temperature[EST], 4);
Serial.write('°'); // Caractère degré
Serial.print(F("C, Ouest: "));
Serial.print(temperature[OUEST], 4);
Serial.write('°'); // Caractère degré
Serial.print(F("C, Nord: "));
Serial.print(temperature[NORD], 4);
Serial.write('°'); // Caractère degré
Serial.println("C, -10, 50");
/* Affiche les températures sur l'écran lcd */
lcdPrint(temperature[PISCINE], 2, 0);
lcdPrint(temperature[EST], 2, 1);
lcdPrint(temperature[OUEST], 11, 1);
lcdPrint(temperature[NORD], 11, 0);
}
#if defined SIMULATION
uint32_t servoTime[NOMBRE_VANNES];
void servoWrite(uint8_t vanneNb, int positionVanne) {
servoTime[vanneNb] = micros();
}
uint16_t analogReadSimu(uint8_t vanneNb) {
uint32_t duration = micros() - servoTime[vanneNb];
// Serial.print("Micro - servoTime[");
// Serial.print(NomVanne[vanneNb]);
// Serial.print("] = ");
// Serial.println(duration);
if (duration < DELAI_DEMARRAGE_SERVO * 250) return 0;
else if (duration < DELAI_DEMARRAGE_SERVO * 800) return COURANT_VANNE_MAX * 4 / 3;
else if (duration < TEMPS_MOUVEMENT_SERVO * 700) return COURANT_VANNE_MAX * 2 / 3;
else return COURANT_VANNE_MIN / 2;
}
#endif
int8_t manageVanne(uint8_t vanneNb) {
static time_t tempsDebutMouvementVanne[NOMBRE_VANNES] = {-1 - QUARANTES_MINUTES, -1 - QUARANTES_MINUTES, -1 - QUARANTES_MINUTES};
static int positionVanne[NOMBRE_VANNES];
static int8_t taskCount = 0;
// static uint16_t courantVanne[NB_CAPTEURs_COURANT];
#if defined SIMULATION
uint16_t courantVanne = analogReadSimu(vanneNb);
// Serial.print("analogRead vanne ");
// Serial.print(NomVanne[vanneNb]);
// Serial.print(" = ");
// Serial.println(courantVanne);
#else
uint16_t courantVanne = analogRead(pinCourantVanne[vanneNb]);
#endif
if (servo[vanneNb].attached()) {
if (tempsDebutMouvementVanne[vanneNb] + DELAI_DEMARRAGE_SERVO < millis() &&
courantVanne > COURANT_VANNE_MAX) {
servo[vanneNb].detach();
tempsDebutMouvementVanne[vanneNb] = -1 - QUARANTES_MINUTES;
taskCount--;
if (taskCount == 0) digitalWrite(LED_BUILTIN, LOW);
Serial.print("Servo activé, consommation de courant anormale vanne ");
Serial.println(NomVanne[vanneNb]);
}
}
else if (COURANT_VANNE_MIN < courantVanne) {
Serial.print("Servo non activé, consommation de courant anormale vanne ");
Serial.println(NomVanne[vanneNb]);
}
// Serial.print(F("manageVanne["));
// Serial.print(NomVanne[vanneNb]);
// Serial.print(F("] Cible = "));
// Serial.print(positionCible[vanneNb]);
// Serial.print(F(", Pos = "));
// Serial.println(positionVanne[vanneNb]);
if (taskCount == 0 && positionVanne[vanneNb] != positionCible[vanneNb]) {
servo[vanneNb].attach(pinServo[vanneNb]);
// StatutVanne[vanneNb] == MOVE;
tempsDebutMouvementVanne[vanneNb] = millis();
digitalWrite(LED_BUILTIN, HIGH);
taskCount++;
positionVanne[vanneNb] = positionCible[vanneNb];
servo[vanneNb].write(positionVanne[vanneNb]); // Ouverture Vanne Est
#if defined SIMULATION
servoWrite(vanneNb, positionVanne[vanneNb]);
#endif
// Serial.print(F("manageVanne "));
// Serial.print(NomVanne[vanneNb]);
// Serial.println(F(" démarre"));
}
else if (tempsDebutMouvementVanne[vanneNb] + TEMPS_MOUVEMENT_SERVO < millis() ||
tempsDebutMouvementVanne[vanneNb] + DELAI_DEMARRAGE_SERVO < millis() &&
courantVanne < COURANT_VANNE_MIN) {
servo[vanneNb].detach();
tempsDebutMouvementVanne[vanneNb] = -1 - QUARANTES_MINUTES;
taskCount--;
if (taskCount == 0) digitalWrite(LED_BUILTIN, LOW);
// Serial.print(F("manageVanne "));
// Serial.print(NomVanne[vanneNb]);
// Serial.println(F(" actionnée"));
}
// Serial.print(F("TaskCount = "));
// Serial.println(taskCount);
lcd.setCursor(6, 0);
lcd.print(taskCount);
return taskCount;
}
void commandeManuelle(uint8_t vanneNb) {
lcd.setCursor(pos[vanneNb], (vanneNb + 1) / 2);
if (digitalRead(PinVanne[vanneNb]) == HIGH) {
positionCible[vanneNb] = VANNE_OUVERTE;
lcd.print('O'); // Vanne Ouverte
}
else {
positionCible[vanneNb] = VANNE_FERMEE;
lcd.print('F'); // Vanne Fermée
}
manageVanne(vanneNb);
}
void regule(uint8_t vanneNb) {
switch (statutVanne[vanneNb]) {
case OUVERTURE_VANNE:
tempsReference[vanneNb] = tempsActuel;
// if (positionCible[vanneNb] != VANNE_OUVERTE) {
positionCible[vanneNb] = VANNE_OUVERTE; // Ouverture vanne
lcd.setCursor(pos[vanneNb], (vanneNb + 1) / 2);
lcd.print('O'); // Vanne Ouverte
Serial.print(F("Vanne "));
Serial.print(NomVanne[vanneNb]);
Serial.println(F(" Ouverture"));
statutVanne[vanneNb] = PAUSE_10_MN;
// }
break;
case PAUSE_10_MN:
if (tempsActuel - tempsReference[vanneNb] > DIX_MINUTES) { // 10 seconde pour le proto
statutVanne[vanneNb] = REGULATION_TEMPERATURE_40_MN_MAX;
Serial.print(F("Vanne "));
Serial.print(NomVanne[vanneNb]);
Serial.println(F(" fin pause 10mn , régulation température"));
}
break;
case REGULATION_TEMPERATURE_40_MN_MAX:
if (temperature[PISCINE] > temperature[vanneNb] || tempsActuel - tempsReference[vanneNb] > QUARANTES_MINUTES - TEMPS_MOUVEMENT_SERVO - MARGE_DE_SECURITE) {
statutVanne[vanneNb] = FERMETURE_VANNE;
Serial.print(F("Vanne "));
Serial.print(NomVanne[vanneNb]);
Serial.println(F(" fin régulation température, fermeture vanne"));
}
break;
case FERMETURE_VANNE:
// if (positionCible[vanneNb] != VANNE_FERMEE) {
positionCible[vanneNb] = VANNE_FERMEE;
lcd.setCursor(pos[vanneNb], (vanneNb + 1) / 2);
lcd.print('F'); // Vanne Fermée
// echeanceFermetureVanne[vanneNb] = tempsActuel + TEMPS_MOUVEMENT_SERVO; // 1 seconde pour fermer vanne
Serial.print(F("Vanne "));
Serial.print(NomVanne[vanneNb]);
Serial.println(F(" fermeture"));
statutVanne[vanneNb] = ATTENTE_NOUVEAU_CYCLE;
// }
// else {
// // Placer ici le code de contrôle de la fermeture de vanne : courant max, temps max...
// if (tempsActuel > echeanceFermetureVanne[vanneNb]) {
// servo[vanneNb].detach();
// statutVanne[vanneNb] = ATTENTE_NOUVEAU_CYCLE;
// Serial.print(F("Vanne "));
// Serial.print(NomVanne[vanneNb]);
// Serial.println(F(" fermée, attente fin du cycle"));
// }
// }
break;
case ATTENTE_NOUVEAU_CYCLE:
if (tempsActuel - tempsReference[vanneNb] > QUARANTES_MINUTES) {
statutVanne[vanneNb] = OUVERTURE_VANNE;
Serial.print(F("Vanne "));
Serial.print(NomVanne[vanneNb]);
Serial.println(F(" redémarrage cycle"));
}
break;
}
manageVanne(vanneNb);
}
short_time_t tempsMesureCapteur;
short_time_t mesureCapteurFaite;
void setup() {
uint8_t sensorAddress[8];
// Start serial communication for debugging purposes
Serial.begin(115200);
sensors.begin();
sensors.setResolution(12);
sensors.setWaitForConversion(false);
sensors.setCheckForConversion(true);
sensors.requestTemperatures();
tempsMesureCapteur = 750 / (1 << (12 - sensors.getResolution()));
mesureCapteurFaite = millis() + tempsMesureCapteur;
/* Ecran LCD */
lcd.init(); // initialisation du LCD
lcd.backlight(); // active le rétroéclairage
lcd.createChar(charAuto, Auto);
lcd.createChar(charManuel, Manuel);
lcd.createChar(charDegC, DegC);
// Affichage de base sur LCD
lcd.setCursor(0, 0); // mettre le curseur à la première colonne, première ligne
lcd.print(F("P= ")); // Piscine
lcd.print((char)charDegC); // °C
lcd.print(F(" N= ")); // plage Nord
lcd.print((char)charDegC); // °C
lcd.setCursor(0, 1); // mettre le curseur à la première colonne, deuxième ligne
lcd.print(F("E= ")); // plage EST
lcd.print((char)charDegC); // °C
lcd.print(F(" O= ")); // plage Ouest
lcd.print((char)charDegC); // °C
pinMode(PinModeManuel, INPUT);
pinMode(PinVanne[EST], INPUT);
pinMode(PinVanne[OUEST], INPUT);
pinMode(PinVanne[NORD], INPUT);
for (uint8_t vanne = 0; vanne < NOMBRE_VANNES; vanne++) {
positionCible[vanne] = VANNE_FERMEE;
manageVanne(vanne);
}
while (millis() < mesureCapteurFaite) {
for (uint8_t vanne = 0; vanne < NOMBRE_VANNES; vanne++) {
manageVanne(vanne);
}
}
sensors.getTempCByIndex(0);
// Set the sensors resolution
sensors.setResolution(12);
sensors.requestTemperatures();
tempsMesureCapteur = 750 / (1 << (12 - sensors.getResolution()));
mesureCapteurFaite = millis() + tempsMesureCapteur;
while (millis() < mesureCapteurFaite) {
for (uint8_t vanne = 0; vanne < NOMBRE_VANNES; vanne++) {
manageVanne(vanne);
}
}
temperature[PISCINE] = sensors.getTempC(SENSOR_PISCINE_ADDRESS); // Température de la piscine
temperature[EST] = sensors.getTempC(SENSOR_EST_ADDRESS); // Température du circuit Est
temperature[OUEST] = sensors.getTempC(SENSOR_OUEST_ADDRESS); // Température du circuit Ouest
temperature[NORD] = sensors.getTempC(SENSOR_OUEST_ADDRESS); // Température du circuit Nord
afficheTemperatures(temperature);
sensors.requestTemperatures();
mesureCapteurFaite = millis() + tempsMesureCapteur;
int taskCount;
do {
delay(10);
for (uint8_t vanne = 0; vanne < NOMBRE_VANNES; vanne++) {
tempsActuel = millis();
taskCount = manageVanne(vanne);
}
} while (taskCount);
Serial.println("Ready");
}
void loop(void) {
tempsActuel = millis(); // Obtient le temps actuel en millisecondes
// Afficher les températures lorsqu'elles sont mesurées et démarrer une nouvelle mesure
if (tempsActuel > mesureCapteurFaite) {
temperature[PISCINE] = sensors.getTempC(SENSOR_PISCINE_ADDRESS); // Température de la piscine
temperature[EST] = sensors.getTempC(SENSOR_EST_ADDRESS); // Température du circuit Est
temperature[OUEST] = sensors.getTempC(SENSOR_OUEST_ADDRESS); // Température du circuit Ouest
temperature[NORD] = sensors.getTempC(SENSOR_NORD_ADDRESS); // Température du circuit Nord
sensors.requestTemperatures();
mesureCapteurFaite = millis() + tempsMesureCapteur; // Utiliser millis() car beaucoup de temps s'est
afficheTemperatures(temperature); // écoulé depuis la MàJ de tempsActuel
}
if (digitalRead(PinModeManuel) == HIGH) {
if (mode != MANUEL) {
lcd.setCursor(7, 0); // mettre le curseur à la sixième colonne, première ligne
lcd.print((char)charManuel); // mode Manuel
mode = MANUEL;
}
for (uint8_t vanne = 0; vanne < NOMBRE_VANNES; vanne++) {
commandeManuelle(vanne);
}
}
else {
// static short_time_t sequenceDemarreVanne;
if (mode != AUTO) {
lcd.setCursor(7, 0);
lcd.print((char)charAuto); // Mode automatique
for (uint8_t vanne = 0; vanne < NOMBRE_VANNES; vanne++) {
statutVanne[vanne] = OUVERTURE_VANNE; // Remise à zéro de la régulation
}
mode = AUTO;
// modeAuto = DEMARRE;
// sequenceDemarreVanne = tempsActuel;
}
// if (modeAuto == DEMARRE) {
// regule(NORD);
// if (tempsActuel > sequenceDemarreVanne + DECALAGE_VANNES) {
// regule(EST);
// if (tempsActuel > sequenceDemarreVanne + 2 * DECALAGE_VANNES) {
// regule(OUEST);
// modeAuto = REGULE;
// }
// }
// }
// else {
for (uint8_t vanne = 0; vanne < NOMBRE_VANNES; vanne++) {
regule(vanne);
}
// }
}
delay(10);
}
T° piscine
T° est
T° ouest
T°nord
est
ouest
nord
Manu
Auto