/************************************************************************************************
* Rafael Jabbour
* École secondaire catholique Renaissance
* ICS4U, Sem 1, 2022-2023
*
* Dernière modification: 15-01-2023
*
* DESCRIPTION:
* Ce programme est un projet Arduino qui contrôle un chauffe-eau. Le projet comporte un capteur de température
* qui mesure la température de l'eau. Une valeur de température de base est choisie par l'utilisateur, pour
* laquelle l'eau doit rester dans cette gamme. Si l'eau descend en dessous de la gamme désirée, une LED bleu
* s'allume indiquant que l'eau est trop froide, et le relais se ferme, permettant à l'eau de commencer à être
* chauffée, puis un autre indicateur LED s'allume pour montrer que le relais est activé. Une fois que l'eau
* atteint la température souhaitée par l'utilisateur, le voyant change de couleur et devient vert, mais le
* relais reste fermé, afin que la température ne descende pas en dessous de la température souhaitée. Si la
* température dépasse la température souhaitée, une LED rouge s'allume, un buzzer se met à sonner, indiquant
* que l'eau est trop chaude, et le relais s'ouvre, ce qui éteint le chauffage et permet à l'eau de refroidir.
* Toutes ces informations sont affichées sur un écran LCD 16 x 2. L'écran LCD commence par l'écran d'introduction
* qui affiche un message de bienvenue à l'utilisateur et lui donne l'instruction d'appuyer sur le bouton Play
* de la télécommande IR pour lancer le programme. Une fois le programme lancé, l'écran LCD présente 6
* interfaces de menu différentes, chacune affichant des informations différentes. Le premier écran affiche
* la température, la température souhaitée en degrés Celsius et l'état du relais (ON/OFF). Le deuxième
* écran affiche les mêmes informations que le premier, mais la température est indiquée en degrés Fahrenheit.
* Le troisième écran affiche une minuterie indiquant la durée de fonctionnement du programme depuis son lancement.
* Le quatrième écran affiche la température moyenne de l'eau en degrés Celsius pour les 60 dernières secondes.
* Le cinquième écran affiche la même chose que le quatrième, mais la température est indiquée en Fahrenheit.
* Enfin, le sixième écran affiche le nom de l'auteur qui a créé le projet. Il y a deux façons pour l'utilisateur
* de régler la température souhaitée. La première consiste à utiliser un potentiomètre. En le tournant vers la
* droite, la température souhaitée augmente, et diminue de gauche. La deuxième façon de modifier la température
* souhaitée est d'utiliser la télécommande IR, qui envoie un signal au récepteur IR connecté à la carte Arduino.
* La télécommande IR est capable de contrôler l'ensemble de l'écran LCD. Premièrement, elle possède un bouton
* d'alimentation qui allume et éteint l'écran LCD. Deuxièmement, l'utilisateur peut utiliser les boutons plus
* et moins pour augmenter ou diminuer la température souhaitée de l'eau. Troisièmement, l'utilisateur peut
* utiliser les flèches avant et arrière pour passer d'un menu à l'autre sur l'écran LCD. Quatrièmement,
* l'utilisateur peut utiliser les chiffres de la télécommande IR pour sélectionner les menus de l'écran, 0 étant
* le premier, et ainsi de suite jusqu'à 5. Les boutons 6 à 9 ne sont pas fonctionnels, et lorsqu'ils sont pressés,
* ils affichent un message d'erreur sur l'écran LCD pendant 2 secondes. Il est important de noter que sur le côté
* droit de chaque menu écran, l'utilisateur est en mesure de voir si le système est allumé ou éteint, c'est-à-dire
* si le relais est fermé ou ouvert, ou en d'autres termes si l'eau est en train d'être chauffée ou non.
*
* HYPOTHÈSES:
* L'utilisateur est en mesure de contrôler l'ensemble du projet en utilisant uniquement la télécommande IR.
* Ou si préféré, l'utilisateur peut également régler la température de l'eau souhaitée à l'aide du potentiomètre.
* Les commandes :
* Potentiomètre : règle la valeur de la température de l'eau souhaitée.
* Bouton marche/arrêt : allume et éteint le LED.
* Bouton plus et moins : augmente et diminue la température désirée.
* Bouton Play : démarre le programme
* Flèches gauche et droite : passer d'un menu à l'autre.
* Chiffres : choisir l'écran qui doit être affiché.
* 0 : affiche la température et la température de consigne en degrés Celsius.
* 1 : montre la température et la température de consigne en Farhenheit
* 2 : montre combien de temps le code a été exécuté
* 3 : montre la température moyenne dans les 60 dernières secondes en degrés Celsius
* 4 : montre la température moyenne des 60 dernières secondes en Farhenheit
* 5 : montre le nom de l'auteur et le nom de famille.
*
************************************************************************************************/
// les bibliothèques incluses
#include <Wire.h> // pour la communication I2C
#include <LiquidCrystal_I2C.h> // pour le LCD
#include <IRremote.h> // pour la télécommande IR
// initialiser la bibliothèque avec les numéros des broches de l'interface
LiquidCrystal_I2C lcd(0x27,16,2); // Réglez l'adresse du LCD à 0x27 pour un affichage de 16 caractères et 2 lignes.
#define temPin A0 //la thermistance s'attache à
#define beta 3950 // le bêta de la thermistance
#define buzzerPin 8 // la broche dans laquelle se trouve le buzzer
#define ledPin 12 // la broche dans laquelle se trouve la led
#define RELAY_PIN 3 // la broche Arduino, qui se connecte à la broche IN du relais
#define PIN_RECEIVER 2 // Broche de signal du récepteur IR
#define REPEAT_READ_DELAY 0x32 // le délai pour la télécommande IR
IRrecv receiver(PIN_RECEIVER); // Objet récepteur IR
decode_results results; // variable pour stocker les résultats de l'IR
const int redPin = 10; // broche rouge pour contrôler la LED RGB
const int bluePin = 13; // broche bleue pour contrôler la LED RGB
const int greenPin = 11; // broche verte pour contrôler la LED RGB
int value = 0; // variable pour stocker les lectures analogiques
float val = 0; // variable permettant de stocker la température de consigne en Celcius.
float valF = 0; // variable pour stocker la température de consigne en Farenheit
float addVal = 0; // variable pour stocker la valeur en utilisant la télécommande IR
float tempC = 0; // variable pour stocker la température en Celcius
float tempF = 0; // variable pour stocker la température en Farenheit
const float tol = 0.25; // Tolérance de température
bool intro = true; // variable pour contrôler l'écran d'introduction
byte currentMenu = 0; // Mémorise le menu de l'écran qui est utilisé
const byte NUMBER_OF_SCREENS = 6; // combien d'écrans de menu il y a
byte currentStatus = 1; // si l'écran est allumé/éteint
const byte ON_OFF = 2; // nombre d'options d'état
String buffer = " Bienvenue a mon Systeme de controle de temperature !"; // tampon pour stocker le texte défilant
int counter = 0; // variable pour stocker le compteur de défilement du texte
int displayContrast = 50; // variable pour stocker le réglage du contraste
const int numReadings = 60; // nombre de relevés de température précédents à sauvegarder
float readings[numReadings]; // tableau pour stocker les lectures de température précédentes
int index = 0; // indice actuel dans le tableau
float total = 0; // total courant des relevés de température
float average = 0; // température moyenne en degrés Celsius
float averageF = 0; // température moyenne en farnheit
unsigned long startTime; // variable pour stocker l'heure de départ
int minutes = 0; // variable pour stocker les minutes
int seconds = 0; // variable pour stocker les secondes
byte customCharPlay[] = {
B10001,
B11001,
B10101,
B10011,
B10011,
B10101,
B11001,
B10001
};
byte customCharTemp[] = {
B00100,
B01010,
B01010,
B01110,
B01110,
B11111,
B11111,
B01110
};
byte customCharEau[] = {
B00100,
B00100,
B01010,
B01010,
B10001,
B10001,
B10001,
B01110
};
byte customCharDry[] = {
B00000,
B10101,
B01110,
B01010,
B11011,
B01010,
B01110,
B10101
};
byte customCharTemp1[] = {
B00000,
B11100,
B00000,
B11000,
B00000,
B11100,
B00000,
B00000
};
byte customCharUser[] = {
B00000,
B01110,
B01110,
B00000,
B01110,
B10001,
B10001,
B11111
};
byte customCharCheck[] = {
B00000,
B00000,
B00001,
B00011,
B10110,
B11100,
B01000,
B00000
};
// symbole de chronometre
byte customCharChrono[] = {
B01110,
B00100,
B01110,
B10101,
B10111,
B10001,
B01110,
B00000
};
void setup()
{
// définissez le nombre de colonnes et de lignes de l'écran LCD :
lcd.init(); //initialiser le lcd
lcd.backlight(); //ouvrir le backlight
receiver.enableIRIn(); // Démarrer le récepteur
Serial.begin(9600); // démarrer la communication série à 9600 bauds
// définit le numéro de chaque pin, et dit si c'est une entrée ou une sortie.
pinMode(buzzerPin, OUTPUT);
pinMode(ledPin, OUTPUT);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
pinMode(A1, INPUT);
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, HIGH);
lcd.createChar(0, customCharChrono);
lcd.createChar(2, customCharTemp);
lcd.createChar(3, customCharTemp1);
lcd.createChar(4, customCharCheck);
lcd.setCursor(0, 1);
// indique à l'utilisateur comment lancer le code
lcd.print("Pour demarrer: ");
lcd.write(0);
// Vérifie si l'utilisateur a lancé le code.
while (intro)
{
// Déplace la phrase d'introduction de droite à gauche, afin que l'utilisateur puisse la lire en entier.
lcd.setCursor(0, 0);
lcd.print(buffer.substring(counter, counter + 16));
counter++;
if (counter + 16 > buffer.length())
{
counter = 0;
}
delay(200);
// Vérifie si l'utilisateur a appuyé sur le bouton de lecture pour lancer le code.
receiverDecode();
}
// Efface ce qui était déjà sur l'écran.
lcd.clear();
// Une fois que l'utilisateur lance le code, un timer démarre
startTime = millis(); //stocke l'heure de début
}
void loop()
{
lcd.setContrast(displayContrast); // définit le contraste de l'affichage
value = analogRead(A1);
delay(100);
long a = 1023 - analogRead(temPin); // lire la valeur de la thermistance
// la formule de calcul de la température à l'aide d'un thermostat
tempC = beta / (log((1025.0 * 10 / a - 10) / 10) + beta / 298.0) - 273.0;
// convertit la valeur du potentiomètre en degré Celcius
val = (value - 32) * 1 / 18;
// assurez-vous de prendre en compte les valeurs de la télécommande et pas seulement le potentiomètre.
val = val + addVal;
// convertit les valeurs de Celcius en Farenheit.
valF = 1.8 * val + 32.0;
tempF = 1.8 * tempC + 32.0;
// appelle la fonction receiverDecode
receiverDecode();
// appeler la fonction screenMenu
screenMenu();
delay(200); // attend 100 millisecondes
// si la température est supérieure à la température de consigne
if (tempC > val + tol)
{
// allumer le buzzer
digitalWrite(buzzerPin, HIGH);
// éteindre le buzzer
digitalWrite(buzzerPin, LOW);
// mettre la LED RGB en rouge
color(0, 255, 255);
digitalWrite(ledPin, HIGH);
// placer le curseur à la colonne 13, ligne 1
lcd.setCursor(13, 1);
lcd.print("OFF");
// placer le curseur à la colonne 13, ligne 1
lcd.setCursor(13, 0);
lcd.print("Sys");
digitalWrite(RELAY_PIN, HIGH);
}
// si la température est inférieure à la température de consigne
else if (tempC < val - tol)
{
color(255, 255, 0);
digitalWrite(ledPin, HIGH);
// placer le curseur à la colonne 13, ligne 1
lcd.setCursor(13, 1);
lcd.print("ON ");
// placer le curseur à la colonne 13, ligne 0
lcd.setCursor(13, 0);
lcd.print("Sys");
digitalWrite(RELAY_PIN, LOW);
}
// si la température est égale à la température de consigne
else if (tempC >= val - tol && tempC <= val + tol)
{
color(255, 0, 255);
digitalWrite(ledPin, HIGH);
digitalWrite(RELAY_PIN, LOW);
}
// s'assure d'effacer l'ancien texte de l'écran.
if (tempC < 10 && tempC > 0)
{
// placer le curseur à la colonne 11, ligne 0
lcd.setCursor(11, 0);
lcd.print(" ");
}
// s'assure d'effacer l'ancien texte de l'écran.
if ((tempC < 0 && tempC > -10) || (tempC < 100 && tempC > 10))
{
// placer le curseur à la colonne 12, ligne 0
lcd.setCursor(12, 0);
lcd.print(" ");
}
// appelle la fonction ReceiverDecode
receiverDecode();
// Exécute l'instruction if toutes les 1 seconde
if (seconds % 1 == 0)
{
// soustraire la dernière mesure
total = total - readings[index];
// stocker la nouvelle mesure
readings[index] = tempC;
// ajouter la nouvelle mesure
total = total + readings[index];
// passe à l'indice suivant dans le tableau
index = (index + 1) % numReadings;
// calculer la température moyenne
average = total / numReadings;
}
averageF = 1.8 * average + 32.0;
//exécute la déclaration toutes les 10 secondes
if (seconds % 10 == 0)
{
Serial.println(average); // afficher la température moyenne dans le débogage
}
//calculer le temps actuel d'exécution du code
unsigned long currentTime = millis() - startTime;
//convertir les millisecondes en minutes
minutes = currentTime / 60000;
//convertir les millisecondes restantes en secondes
seconds = (currentTime % 60000) / 1000;
}
// C'est la méthode qui reçoit le signal de la télécommande IR et le convertit en données utilisables.
void translateIR()
{
// Prend la commande basée sur le code IR reçu
switch (receiver.decodedIRData.command)
{
case 162:
Serial.println("POWER");
currentStatus++;
screenStatus();
break;
case 226:
Serial.println("MENU");
break;
case 34:
Serial.println("TEST");
break;
case 2:
Serial.println("PLUS");
addVal++;
break;
case 194:
Serial.println("BACK");
break;
case 224:
Serial.println("PREV.");
currentMenu--;
screenMenu();
break;
case 168:
Serial.println("PLAY");
intro = false;
break;
case 144:
Serial.println("NEXT");
currentMenu++;
screenMenu();
break;
case 152:
Serial.println("MINUS");
addVal--;
break;
case 176:
Serial.println("key: C");
displayContrast--; // Diminue le contraste de l'écran
break;
case 104: Serial.println("num: 0"); currentMenu = 0; break;
case 48: Serial.println("num: 1"); currentMenu = 1; break;
case 24: Serial.println("num: 2"); currentMenu = 2; break;
case 122: Serial.println("num: 3"); currentMenu = 3; break;
case 16: Serial.println("num: 4"); currentMenu = 4; break;
case 56: Serial.println("num: 5"); currentMenu = 5; break;
case 90: Serial.println("num: 6"); ButtonNotSet(); break;
case 66: Serial.println("num: 7"); ButtonNotSet(); break;
case 74: Serial.println("num: 8"); ButtonNotSet(); break;
case 82: Serial.println("num: 9"); ButtonNotSet(); break;
default:
Serial.println(
"Impossible de traduire : Vous appuyez sur deux boutons ou plus.");
break;
}
// délai pour le délai avant que l'utilisateur puisse appuyer sur un autre bouton
delay(REPEAT_READ_DELAY);
}
// Cette méthode définit l'interface de chaque écran de menu que l'utilisateur peut voir.
void screenMenu()
{
// retour en boucle au menu 0 après le dernier menu
currentMenu = currentMenu % NUMBER_OF_SCREENS;
// Cette opération vérifie quel écran l'utilisateur devrait voir et l'affiche.
switch (currentMenu)
{
case 0:
lcd.setCursor(0, 0);
// indique à l'utilisateur comment lancer le code
lcd.write(2);
lcd.write(3);
lcd.print(":");
// Imprimez la température en degrés Celsius sur l'écran LCD.
lcd.print(tempC);
//imprimer l'unité ℃
lcd.write(char(223));
lcd.print("C");
// placer le curseur à la colonne 0, ligne 1
lcd.setCursor(0, 1);
lcd.setCursor(0, 1);
// indique à l'utilisateur comment lancer le code
lcd.write(4);
lcd.write(2);
lcd.write(3);
lcd.print(":");
// Imprimez la température réglée en degrés Celsius sur l'écran LCD.
lcd.print(val);
//imprime l'unité ℃
lcd.write(char(223));
lcd.print("C");
break;
case 1:
// place the cursor in column 0, line 0
lcd.setCursor(0, 0);
// Affiche la température en farenheit sur l'écran LCD.
lcd.print("Temp:");
lcd.print(tempF);
//Affiche l'unité °F
lcd.write(char(223));
lcd.print("F");
// placer le curseur à la colonne 0, ligne 1
lcd.setCursor(0, 1);
lcd.print("Fixe:");
// Affiche la température de consigne en farhrenheit sur l'écran LCD.
lcd.print(valF);
//Affiche l'unité °F
lcd.write(char(223));
lcd.print("F");
break;
case 2:
// place the cursor in column 0, line 0
lcd.setCursor(0, 0);
lcd.print("Temps ecoule:");
// placer le curseur à la colonne 0, ligne 1
lcd.setCursor(0, 1);
//affichage de l'heure actuelle sur le lcd
lcd.print(minutes);
lcd.print(":");
lcd.print(seconds);
lcd.println(" min ");
break;
case 3:
// placer le curseur à la colonne 0, ligne 0
lcd.setCursor(0, 0);
lcd.println("Moy tempC:");
// set the cursor to column 0, line 1
lcd.setCursor(0, 1);
// display the average temperature
lcd.print(average);
//print the unit ℃
lcd.write(char(223));
lcd.print("C ");
break;
case 4:
// placer le curseur à la colonne 0, ligne 0
lcd.setCursor(0, 0);
lcd.println("Moy tempF:");
// placer le curseur à la colonne 0, ligne 1
lcd.setCursor(0, 1);
// afficher la température moyenne
lcd.print(averageF);
//imprime l'unité °F
lcd.write(char(223));
lcd.print("F ");
break;
case 5:
// placer le curseur à la colonne 0, ligne 0
lcd.setCursor(0, 0);
lcd.print("auteur : ");
// placer le curseur à la colonne 0, ligne 1
lcd.setCursor(0, 1);
//affiche le nom de l'auteur sur le lcd
lcd.println("Rafael J. ");
break;
default:
// placer le curseur à la colonne 0, ligne 0
lcd.setCursor(0, 0);
lcd.println("ce bouton nest pas regle pour effectuer une tache");
break;
}
}
// Cette méthode est utilisée pour décoder le signal IR envoyé par la télécommande et reçu par le récepteur.
void receiverDecode()
{
// si le signal IR est reçu
if (receiver.decode())
{
Serial.println(results.value); // afficher dans le débogage le bouton qui a été pressé
// call translateIR function
translateIR();
// Recevoir la valeur suivante
receiver.resume();
}
}
// Cette méthode permet d'allumer et d'éteindre l'écran du lcd.
void screenStatus()
{
// boucle l'allumage et l'extinction de l'écran
currentStatus = currentStatus % ON_OFF;
// Vérifie si l'état de l'écran est 1 (allumé) ou 2 (éteint).
switch (currentStatus)
{
case 0:
lcd.noDisplay();
break;
case 1:
lcd.display();
break;
}
}
// Donne un message à l'écran pendant deux secondes indiquant que le bouton de la télécommande IR n'a pas été codé.
void ButtonNotSet()
{
// efface l’écran
lcd.clear();
// placer le curseur à la colonne 0, ligne 0
lcd.setCursor(0, 0);
lcd.println("ce bouton nest ");
// placer le curseur à la colonne 0, ligne 1
lcd.setCursor(0, 1);
lcd.println("pas regle! ");
delay(2000);
}
// cette méthode est utilisée pour contrôler la led rgb, et pour changer ses couleurs dans le code
void color(unsigned char red, unsigned char green, unsigned char blue)
{
analogWrite(redPin, red);
analogWrite(bluePin, blue);
analogWrite(greenPin, green);
}