Bonjour,
Le projet est maintenant terminé. Il reste à placer les composants dans un boîtier.
Ce thermostat fonctionnant via une liaison Internet est destiné à remplacer un projet qui fonctionne depuis 2017 avec un arduino Yun (ver 1). (Contact "sec" par relais)
Le challenge était d'utiliser du matériel de fond de tiroir (Uno R3, shield Ethernet, RTC, relais, sonde DHT22, diodes led et quelques résistances).
Le remplacement d'un Yun par un Uno engendre évidemment quelques compromis.
La capacité mémoire très limitée du Uno, l'absence d'un système Linux présent sur le Yun, l'obligation de passer par un DynDns, l'absence d'un serveur web local (sur le Uno), une sécurité des transmissions assez faible et une certaine lenteur d'échanges de données.
Solution mise en oeuvre sur le Uno : un serveur d'application json et un traitement des données reçues du client (le client est un serveur web avec scripts php tournant sur un Raspberry pi 3B+ pour les essais). La sécurité est rudimentaire et consiste à utiliser une clé de caractères d'identification.
Si le projet intéresse, je peux publier les codes. (le sketch sur le Uno et le script php sur le serveur web (Apache sur Raspberry pour les essais)).
Le matériel :
1 Arduino Uno R3
1 sonde DHT22
1 module RTC avec pile DS3231
3 résistances de 220 Ohm
1 résistance de 8 kOhm (de 4,7 à 10 kOhm)
3 led (1 verte, 1 jaune, 1 rouge)
1 relais (contact sec avec tension de 220V au secondaire - déclenche le chauffage)
1 condensateur de filtrage de 100 nF
1 ethernet shield avec lecteur carte sd
1 carte sd faible capacité formatée en Fat
Le code pour le Uno :
[code]
#include <SPI.h>
#include <Ethernet.h>
#include "uRTCLib.h"
#include <SD.h>
#include <DHT.h>
#define brocheBranchementDHT 5
#define typeDeDHT DHT22
uRTCLib rtc(0x68);
File filetherm;
// the media access control (ethernet hardware) address for the shield:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
//the IP address for the shield:
byte ip[] = { XXX,XXX,XXX,XXX }; // introduire l'adresse IP locale du shield Ethernet
// the router's gateway address:
byte gateway[] = { XXX,XXX,XXX,XXX }; // introduire l'adresse IP du routeur local
// the subnet:
byte subnet[] = { 255, 255, 255, 0 };
// server sur port XXXX à introduire en fonction des autorisations de l'hébergeur choisi
EthernetServer server (XXXXX);
char *url = (char *)malloc(10); // L'url recu à stocker
int index = 0;// index indiquant où l'on est rendu dans la chaîne
int maxindex = 0;
char carlu;
boolean ouvert_ferme = false;// true quand relais est fermé
/* 9 valeurs par défaut des 9 variables - index de 0 à 8
0 - int temp_jour_uni = 20;//2 chiiffres max
1 - int temp_nuit_uni = 7:// 2 chiffres max
2 - int heure_debut_jour = 8;// 2 chiffres max
3 - int min_debut_jour = 0;//2 chiffres max
4 - int heure_debut_nuit = 22;// 2 chiffres max
5 - int min_debut_nuit = 30;// 2 chiffres max
6 - int therm_actif = 0;//1 chiffre 0=non-actif 1=actif
7 - int type_programme 0 1 2 3 = 0;// 1 chiffre
8 - int relai ouvert ou fermé = 0 // 1 chiffre 0=ouvert 1=fermé (cette valeur est uniquement en sortie)
*/
int varint[9];//={20,10,8,0,22,30,0,0,0};// valeurs par défaut;
float temp_sonde;
DHT dht(brocheBranchementDHT, typeDeDHT);
float hyst=0;//valeur hystérisis 0 ou 0.5 -> 0 au setup
void setup()
{
//// On démarre la voie série pour déboguer
Serial.begin(9600);
delay(2000); // wait for console opening
// initialise ethernet device
Ethernet.begin(mac, ip, gateway, subnet);
// start listening for clients
server.begin();
delay(2000); // wait for server opening
boolean lecture=true;
if (!SD.begin(4)) {
//Serial.println(F("Initialisation faute!"));
//// si la lecture des 9 données int sur la carte SD n'est pas possible
//// valeurs par défaut
lecture=false;
}else{
if (!lecture_sd()){lecture=false;}}
if (lecture == false){varint[0]=20;varint[1]=10;varint[2]=8;varint[3]=0;varint[4]=23;varint[5]=0;varint[6]=0;varint[7]=0;varint[8]=0;}
URTCLIB_WIRE.begin();
////Comment out below line once you set the date & time.
//// Following line sets the RTC with an explicit date & time
//// for example to set January 13 2022 at 12:56 you would call:
//// rtc.set(0, 28, 14, 7, 2, 12, 23);
//// rtc.set(second, minute, hour, dayOfWeek, dayOfMonth, month, year)
//// set day of week (1=Sunday, 7=Saturday)
// Initialisation du DHT22;
dht.begin();
temp_sonde = dht.readTemperature();
// pin 4 réservée à SD
// pin 5 réservée à DHT22
pinMode(6, OUTPUT);// relais
pinMode(7, OUTPUT);//led verte
pinMode(8, OUTPUT);//led jaune
pinMode(9, OUTPUT);//led rouge
// pin 10,11,12,13 réservées au Shield Ethernet
digitalWrite(7, HIGH);// led verte allumée
digitalWrite(8, LOW);// led jaune éteinte
digitalWrite(9, LOW);// led rouge éteinte
}
void loop()
{
delay(2000);
rtc.refresh();
EthernetClient client = server.available();
if(client.available()){
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: application/json;charset=utf-8");
client.println("Server: Arduino");
client.println("Connection: close");
// traitement des infos du client
index=0;
while(carlu != '\n') {
carlu = client.read(); //on lit ce qu'il raconte
//Serial.print(carlu);
url[index] = carlu;
index++;
}
//Serial.print(F("\nindex="));
//Serial.println(index);
maxindex=index-11;
carlu = '#';// obligatoire pour le cycle suivant du while
client.println();// ligne vide obligatoire
if (index == 46 || index == 26 || index == 50){
if (interpreter() == false){page_fin(client);exit;}
}
else{
page_fin(client);exit;
}
client.println(F("{"));
client.print(F("\"jour\":"));
client.print(rtc.day());// indice 0
client.print(F(",\"mois\":"));
client.print(rtc.month());// indice 1
client.print(F(",\"annee\":"));
client.print(rtc.year()); // indice 2
client.print(F(",\"heure\":"));
client.print(rtc.hour()); // indice 3
client.print(F(",\"minute\":"));
client.print(rtc.minute()); // indice 4
client.print(F(",\"temp_ambiante\":"));
client.print(temp_sonde);// indice 5
client.print(F(",\"temp_jour\":"));
client.print(varint[0]);// indice 6 sortie/rentrée
client.print(F(",\"temp_nuit\":"));
client.print(varint[1]);// indice 7 sortie/rentrée
client.print(F(",\"heure_debut_jour\":"));
client.print(varint[2]);// indice 8 sortie/rentrée
client.print(F(",\"minute_debut_jour\":"));
client.print(varint[3]);// indice 9 sortie/rentrée
client.print(F(",\"heure_debut_nuit\":"));
client.print(varint[4]);// indice 10 sortie/rentrée
client.print(F(",\"minute_debut_nuit\":"));
client.print(varint[5]);// indice 11 sortie/rentrée
client.print(F(",\"therm_actif\":"));
client.print(varint[6]);// indice 12 sortie/rentrée
client.print(F(",\"Prog_choix\":"));
client.print(varint[7]);// indice 13 sortie/rentrée
client.print(F(",\"relai\":"));
client.print(varint[8]);// indice 14 sortie
client.print(F("}"));
delay(1000);
//Serial.println(F("client stop"));
client.stop();
}
if (varint[6]==1) // si thermostat actif
{
//Serial.println(F("Actions declenchees ..."));
digitalWrite(8, HIGH);// led jaune allumée
long temp_demande_jour = varint[0];
long temp_demande_nuit = varint[1];
int heure_min_jour_debut = varint[2]*60+varint[3];
int heure_min_nuit_debut = varint[4]*60+varint[5];
int Heure_min_actuel = rtc.hour()*60+rtc.minute();
varint[8]=0;ouvert_ferme=false;// relai ouvert
switch (varint[7]){
case 0:
if (heure_min_jour_debut > Heure_min_actuel){
Serial.println("on est la nuit");
if (temp_demande_nuit - hyst > temp_sonde){varint[8]=1;
if (ouvert_ferme==false){ferme_ouvre_relais(true);ouvert_ferme=true;hyst=0;}
}
else{ferme_ouvre_relais(false);ouvert_ferme=false;hyst=0.5;}
}
if (heure_min_jour_debut < Heure_min_actuel && Heure_min_actuel <= heure_min_nuit_debut){
//Serial.println("on est le jour");
if (temp_demande_jour - hyst > temp_sonde){varint[8]=1;
if (ouvert_ferme==false){ferme_ouvre_relais(true);ouvert_ferme=true;hyst=0;}
}
else{ferme_ouvre_relais(false);ouvert_ferme=false;hyst=0.5;}
}
if (heure_min_nuit_debut < Heure_min_actuel && Heure_min_actuel >= heure_min_jour_debut){
//Serial.println("on est la nuit");
if (temp_demande_nuit - hyst > temp_sonde){varint[8]=1;
if (ouvert_ferme==false){ferme_ouvre_relais(true);ouvert_ferme=true;hyst=0;}
}
else{ferme_ouvre_relais(false);ouvert_ferme=false;hyst=0.5;}
}
break;
case 1:
if (temp_demande_jour - hyst > temp_sonde){varint[8]=1;
if (ouvert_ferme==false){ferme_ouvre_relais(true);ouvert_ferme=true;hyst=0;}
}
else{ferme_ouvre_relais(false);ouvert_ferme=false;hyst=0.5;}
//Serial.print(F("t.de la sonde = "));
//Serial.println(temp_sonde);
//Serial.print(F("t.demandée - hyst = "));
//float tempa = temp_demande_jour - hyst;
//Serial.println(tempa);
break;
case 2:
if (temp_demande_nuit - hyst > temp_sonde){varint[8]=1;
if (ouvert_ferme==false){ferme_ouvre_relais(true);ouvert_ferme=true;hyst=0;}
}
else{ferme_ouvre_relais(false);ouvert_ferme=false;hyst=0.5;}
break;
case 3:
if (5 - hyst > temp_sonde){varint[8]=1;
if (ouvert_ferme==false){ferme_ouvre_relais(true);ouvert_ferme=true;hyst=0;}
}
else{ferme_ouvre_relais(false);ouvert_ferme=false;hyst=0.5;}
break;
}
}else{digitalWrite(8, LOW);ferme_ouvre_relais(false);ouvert_ferme=false;}// led jaune éteinte/relais ouvert/led rouge éteinte
//delay(3000);
temp_sonde = dht.readTemperature();
}
boolean interpreter() {
//Serial.println(F("interpretation"));
int jj=0;
boolean chgb = true;
char k[9] = {'k','a','b','c','d','e','f','g','h'};// clé de connexion à changer !!!!
for (byte i=index-20;i<index-11;i++){
//Serial.print(url[i]);
//Serial.println(k[jj]);
if (url[i] != k[jj]){chgb = false;}
jj++;
}
// la clé n'est pas valide
if (chgb == false){
//Serial.println(F("La cle n'est pas valide"));
return false;
}
// fin du traitement du simple aperçu avec simplement la clé approuvée
if (index == 26){return true;}
// traitement de l'ajustage de l'heure et de la date à partir du client connecté
// 5 grandeurs à traiter de 2 chiffres pour former un nombre int donc chaîne de 14 caractères
// GET /?d
if (index == 50){
int varintT[7]; // les 7 variables à introduire
//Serial.println(F("Ajustage du temps"));
jj = 7; // passe les 7 premiers caracteres
for (byte i = 0; i < 7; i++)
{
//int chg = varint[i];//valeur de départ
varintT[i] = int(url[jj] - '0');
varintT[i] = varintT[i]*10 + int(url[jj+1] - '0');
//Serial.println(varintT[i]);
jj=jj+2;
}
rtc.set(varintT[0],varintT[1],varintT[2],varintT[3],varintT[4],varintT[5],varintT[6]);
return true;
}
// chaîne de 14 chiffres après 10 caractères GET /?com:
jj = 10; // passe les 10 premiers caractères - 18 caractères suivent = 28 caractères en tout < 29
chgb=false;
Serial.println(F("Changement de reglage"));
for (byte i = 0; i < 8; i++)
{
int chg = varint[i];//valeur de départ
varint[i] = int(url[jj] - '0');
varint[i] = varint[i]*10 + int(url[jj+1] - '0');
if (chg != varint[i]){
Serial.print(F("valeur change "));
Serial.print(chg);
Serial.print(F(" <> "));
Serial.println(varint[i]);
chgb = true;
}
Serial.println(varint[i]);
jj=jj+2;
}
if(chgb){ecriture_sd();}
delay(2000);
return true;
}
boolean lecture_sd(){
//Serial.println(F("Lecture des donnees sur SD"));
filetherm = SD.open("THERM.TXT");
if (filetherm) {
//Serial.write(filetherm.read());
int jj=0;
int ii=0;
int chiffre[2];
while (filetherm.available()){
char c = filetherm.read();
if (c != 44){
//Serial.print(c);
if (jj==1){
chiffre[1] = int(c - '0');// jj = 1 2eme chiffre
varint[ii]=varint[ii]*10+chiffre[1];// 1er chiffre autoatiquement multiplié par 10 + 2ème chifffre unitaire
}
if (jj==0){
chiffre[jj] = int(c - '0');// jj = 0 1er chiffre
varint[ii]=chiffre[0];// 1er chiffre
jj=1;
}
}
if (c == 44){
jj=0;// jj reprend la valeur 0
//Serial.println (varint[ii]);
ii=ii+1;
}
if (c == '\n'){break;}
}
// close the file:
filetherm.close();
//Serial.println(F("Fin de lecture"));
return true;
} else {
//Serial.println(F("erreur ouverture de therm.txt"));
return false;
}
delay(1000);
return true;
}
boolean ecriture_sd(){
//Serial.println(F("Ecriture des donnees sur SD"));
hyst=0;// remet à 0
filetherm = SD.open("THERM.TXT",FILE_WRITE);
if (filetherm) {
String valeurs = "";
for (byte ii=0 ; ii < 9 ; ii++){
valeurs=valeurs+varint[ii]+",";
}
filetherm.println(valeurs);
// close the file:
filetherm.close();
//Serial.println(F("Fin d'ecriture"));
return true;
} else {
// if the file didn't open, print an error:
//Serial.println(F("Erreur d'ouverture du fichier"));
return false;
}
return true;
}
boolean page_fin(EthernetClient client){
client.print("Authentification requise !");
client.stop();
}
boolean ferme_ouvre_relais(boolean etat){
// change l'état du relai sur pin 6
if (etat == true){digitalWrite(6, LOW);digitalWrite(9, HIGH);Serial.println("relai ferme");}// led rouge allumée
else{digitalWrite(6, HIGH);digitalWrite(9, LOW);Serial.println("relai ouvert");}// led rouge éteinte
}
[/code]
Le fichier zip avec les pages php à placer sur le serveur web (apache avec protection).
Le serveur web d'essai est placé sur un Raspberry pi3b+.
pages_web.zip (11.6 KB)