// Blocage Servo Moteur
// https://forum.arduino.cc/t/blocage-servo-moteur/1305482

#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};

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

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

/* 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];
uint32_t tempsActuel;
uint32_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);
}

int8_t actionneVanne(uint8_t vanneNo) {
  static uint32_t echeanceMouvementVanne[NOMBRE_VANNES];
  static int positionVanne[NOMBRE_VANNES];
  static int8_t taskCount = 0;

  // Serial.print(F("actionneVanne["));
  // Serial.print(NomVanne[vanneNo]);
  // Serial.print(F("] Cible = "));
  // Serial.print(positionCible[vanneNo]);
  // Serial.print(F(", Pos = "));
  // Serial.println(positionVanne[vanneNo]);
  if (/*echeanceMouvementVanne[vanneNo] == -1 && */positionVanne[vanneNo] != positionCible[vanneNo]) {
    servo[vanneNo].attach(pinServo[vanneNo]);
    // StatutVanne[vanneNo] == MOVE;
    echeanceMouvementVanne[vanneNo] = millis() + TEMPS_MOUVEMENT_SERVO;
    digitalWrite(LED_BUILTIN, HIGH);
    taskCount++;
    positionVanne[vanneNo] = positionCible[vanneNo];
    servo[vanneNo].write(positionVanne[vanneNo]); // Ouverture Vanne Est
    // Serial.print(F("actionneVanne "));
    // Serial.print(NomVanne[vanneNo]);
    // Serial.println(F(" démarre"));
  }
  else if (echeanceMouvementVanne[vanneNo] < millis()) {
    servo[vanneNo].detach();
    echeanceMouvementVanne[vanneNo] = -1;
    taskCount--;
    if (taskCount == 0) digitalWrite(LED_BUILTIN, LOW);
    // Serial.print(F("actionneVanne "));
    // Serial.print(NomVanne[vanneNo]);
    // 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 vanneNo) {
  lcd.setCursor(pos[vanneNo], (vanneNo + 1) / 2);
  if (digitalRead(PinVanne[vanneNo]) == HIGH) {
    positionCible[vanneNo] = VANNE_OUVERTE;
    lcd.print('O');  // Vanne Ouverte
  }
  else {
    positionCible[vanneNo] = VANNE_FERMEE;
    lcd.print('F');  // Vanne Fermée
  }
  actionneVanne(vanneNo);
}

void regule(uint8_t vanneNo) {
  switch (statutVanne[vanneNo]) {
    case OUVERTURE_VANNE:
      if (positionCible[vanneNo] != VANNE_OUVERTE) {
        positionCible[vanneNo] = VANNE_OUVERTE;  // Ouverture vanne
        tempsReference[vanneNo] = tempsActuel;
        lcd.setCursor(pos[vanneNo], (vanneNo + 1) / 2);
        lcd.print('O');  // Vanne Ouverte
        Serial.print(F("Vanne "));
        Serial.print(NomVanne[vanneNo]);
        Serial.println(F(" Ouverture"));
        statutVanne[vanneNo] = PAUSE_10_MN;
      }
      break;

    case PAUSE_10_MN:
      if (tempsActuel - tempsReference[vanneNo] > DIX_MINUTES) { // 10 seconde pour le proto
        statutVanne[vanneNo] = REGULATION_TEMPERATURE_40_MN_MAX;
        Serial.print(F("Vanne "));
        Serial.print(NomVanne[vanneNo]);
        Serial.println(F(" fin pause 10mn , régulation température"));
      }
      break;

    case REGULATION_TEMPERATURE_40_MN_MAX:
      if (temperature[PISCINE] > temperature[vanneNo] || tempsActuel - tempsReference[vanneNo] > QUARANTES_MINUTES - TEMPS_MOUVEMENT_SERVO - MARGE_DE_SECURITE) {
        statutVanne[vanneNo] = FERMETURE_VANNE;
        Serial.print(F("Vanne "));
        Serial.print(NomVanne[vanneNo]);
        Serial.println(F(" fin régulation température, fermeture vanne"));
      }
      break;

    case FERMETURE_VANNE:
      if (positionCible[vanneNo] != VANNE_FERMEE) {
        positionCible[vanneNo] = VANNE_FERMEE;
        lcd.setCursor(pos[vanneNo], (vanneNo + 1) / 2);
        lcd.print('F');  // Vanne Fermée
        // echeanceFermetureVanne[vanneNo] = tempsActuel + TEMPS_MOUVEMENT_SERVO; // 1 seconde pour fermer vanne
        Serial.print(F("Vanne "));
        Serial.print(NomVanne[vanneNo]);
        Serial.println(F(" fermeture"));
        statutVanne[vanneNo] = ATTENTE_NOUVEAU_CYCLE;
      }
      // else {
      //   // Placer ici le code de contrôle de la fermeture de vanne : courant max, temps max...
      //   if (tempsActuel > echeanceFermetureVanne[vanneNo]) {
      //     servo[vanneNo].detach();
      //     statutVanne[vanneNo] = ATTENTE_NOUVEAU_CYCLE;
      //     Serial.print(F("Vanne "));
      //     Serial.print(NomVanne[vanneNo]);
      //     Serial.println(F(" fermée, attente fin du cycle"));
      //   }
      // }
      break;

    case ATTENTE_NOUVEAU_CYCLE:
      if (tempsActuel - tempsReference[vanneNo]  > QUARANTES_MINUTES) {
        statutVanne[vanneNo] = OUVERTURE_VANNE;
        Serial.print(F("Vanne "));
        Serial.print(NomVanne[vanneNo]);
        Serial.println(F(" redémarrage cycle"));
      }
      break;
  }
  actionneVanne(vanneNo);
}

uint32_t tempsMesureCapteur;
uint32_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;
    actionneVanne(vanne);
  }

  while (millis() < mesureCapteurFaite);
  sensors.getTempCByIndex(0);

  // Set the sensors resolution
  sensors.setResolution(12);
  sensors.requestTemperatures();
  delay(tempsMesureCapteur);
  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 = actionneVanne(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 uint32_t sequenceDemarreVanne;

    if (mode != AUTO) {
      lcd.setCursor(7, 0);
      lcd.print((char)charAuto);  // Mode automatique
      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);
}
Loading
ds18b20
T° piscine
Loading
ds18b20
T° est
Loading
ds18b20
T° ouest
Loading
ds18b20
T°nord
est
ouest
nord
Manu
Auto