Système d’irrigation via WiFi
Ce projet consiste à concevoir un système d’irrigation intelligent avec une pompe immergé dans l’eau pour arroser la plante lorsque le sol est sec et surveille en temps réel l’humidité du sol de la plante, l’humidité et la température ambiante. Les données sont affichées sur un écran LCD ainsi qu’une interface web hébergée sur un serveur local.

Matériel
- Breadboard
- Arduino UNO R4 WiFi
- Afficheur LCD 16*2
- Capteur de température et humidité(DHT22)
- Capteur de l’humidité du sol(terre)
- Module de Relai SRS-05VDC-SL
- Circuit intégré TA6586
- Pompe à eau
- Piles AA
- Fils jumper mâles
- Tubes
- Bocal en impression 3D
- Pot
- Plante
- Aimants
- Imprimante 3D
Illustration du projet
Vue de côté

Vue arrière

Vue intérieur du boitier

Schéma Bloc
Voici le schéma du projet

Caractéristiques des pièces


Circuit électronique

Description Général Du Projet
Le capteur d’humidité du sol s’assure que le seuil de la terre n’est plus bas que 41%. Si on obtient un seuil plus bas que 40%, alors la pompe va s’activer pour arroser la terre pendant 2 secondes consécutives avant de s’arrêter pour recalculer le taux d’humidité à nouveau. Si le seuil idéal n’est toujours pas atteint, la pompe va recommencer son arrosage jusqu’à ce qu’on obtient le taux d’humidité souhaité.
L’écran LCD nous permet de voir le changement de température de la pièce, le taux d’humidité de la terre et l’adresse IP de La carte Arduino. Grâce à l’adresse IP on a accès à la page web du projet. Là dessus on peut non seulement voir la température et le taux d’humidité de la terre, mais on peut aussi contrôler la pompe manuellement. Tout ça sur notre téléphone!
Description Technique Des Composants
- Arduino UNO R4 WiFi : La carte électronique lit les capteurs, envoie les données, prend les décisions et communique avec le serveur internet.
- Capteur d’humidité du sol : Il mesure le niveau d’humidité du sol sous forme de valeur analogique.
- DHT22 : C’est un capteur qui mesure la température et l’humidité de l’air. Il permet d’avoir un suivi des conditions environnementales de la pièce.
- Afficheur LCD : Il affiche localement les informations des capteurs tel qu’humidité du sol, température, état de la pompe et de l’adresse IP du serveur.
- TA6586 CI : Puce de contrôle de moteur qui sert de module de relais. Le circuit intégrer agit comme un interrupteur commandé par Arduino qui permet d’activer la pompe en toute sécurité.
- Serveur local : Il gère la communication entre le téléphone et le système. Il envoie les données en temps réel.
- Téléphone : Il permet d’accéder à l’interface du système WiFi. Il sert a visualiser les données de la température, humidité du sol et de la pièce.
- Pompe à eau : Pompe l’eau depuis la source et l’envoie vers la plante lorsque le relais est activé.
- Source d’eau : Contenant d’eau utilisé pour circuler dans la pompe qui servira à rendre humide la terre de la plante.
- Plante : La plante reçois l’eau nécessaire selon les décisions prises par l’Arduino
Conception 3D
Boitier du circuit électronique Boitier de l’écran LCD Contenant pour l’eau



Programmation
On a programmé avec Arduino, le programme se trouve sur le lien ci dessous:
#include <Adafruit_Sensor.h>
#include <WiFiS3.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
// =======================
// WIFI (connexion au réseau + serveur web)
// =======================
char ssid[] = "TCOM2026";
char pass[] = "TCOM2026";
WiFiServer server(80);
int status = WL_IDLE_STATUS;
// =======================
// MATÉRIEL (broches et capteurs utilisés)
// =======================
#define DHTPIN 4
#define DHTTYPE DHT22
#define SOIL_MOISTURE_PIN A0
#define WATER_PUMP_PIN 8
// =======================
// CALIBRATION DU CAPTEUR D'HUMIDITÉ DU SOL
// =======================
const int SOIL_DRY = 700; // valeur brute quand le sol est sec
const int SOIL_WET = 300; // valeur brute quand le sol est très humide
const int DRY_THRESHOLD = 40; // seuil (%) pour déclencher l’arrosage en mode AUTO
// =======================
// PARAMÈTRES DE TEMPS (en millisecondes)
// =======================
const unsigned long PUMP_ON_MS = 1000; // durée d’activation de la pompe
const unsigned long SENSOR_MS = 2000; // intervalle de lecture des capteurs
const unsigned long LCD_MS = 5000; // intervalle de rafraîchissement de l’écran LCD
// =======================
// OBJETS DES COMPOSANTS
// =======================
LiquidCrystal_I2C lcd(0x27, 16, 2);
DHT dht(DHTPIN, DHTTYPE);
// =======================
// VARIABLES DES MESURES
// =======================
float temperature = 0.0;
float humidity = 0.0;
int soilRaw = 0;
int soilPercent = 0;
bool dhtOk = false;
// =======================
// MODES DE FONCTIONNEMENT DE LA POMPE
// =======================
enum PumpMode { AUTO, MANUAL_ON, MANUAL_OFF };
PumpMode pumpMode = AUTO;
bool pumpIsOn = false;
unsigned long pumpStartMs = 0;
// =======================
// MINUTERIES
// =======================
unsigned long lastSensorMs = 0;
unsigned long lastLcdMs = 0;
int lcdScreen = 0;
// =======================
// FONCTIONS UTILITAIRES
// =======================
int soilToPercent(int raw) {
raw = constrain(raw, SOIL_WET, SOIL_DRY);
return map(raw, SOIL_DRY, SOIL_WET, 0, 100);
}
const char* modeText() {
if (pumpMode == AUTO) return "AUTO";
if (pumpMode == MANUAL_ON) return "MANUAL ON";
return "MANUAL OFF";
}
// =======================
// ÉCRAN LCD
// =======================
void lcdConnect() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Connexion WiFi");
lcd.setCursor(0, 1);
lcd.print(ssid);
}
void lcdIP() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("IP:");
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
}
// =======================
// LECTURE DES CAPTEURS
// =======================
void readSensors() {
humidity = dht.readHumidity();
temperature = dht.readTemperature();
soilRaw = analogRead(SOIL_MOISTURE_PIN);
soilPercent = soilToPercent(soilRaw);
dhtOk = !(isnan(temperature) || isnan(humidity));
}
// =======================
// GESTION DE LA POMPE
// =======================
void handlePump(unsigned long now) {
if (pumpMode == MANUAL_ON) {
digitalWrite(WATER_PUMP_PIN, HIGH);
pumpIsOn = true;
return;
}
if (pumpMode == MANUAL_OFF) {
digitalWrite(WATER_PUMP_PIN, LOW);
pumpIsOn = false;
return;
}
// Mode AUTO
if (soilPercent < DRY_THRESHOLD && !pumpIsOn) {
digitalWrite(WATER_PUMP_PIN, HIGH);
pumpIsOn = true;
pumpStartMs = now;
}
if (pumpIsOn && (now - pumpStartMs >= PUMP_ON_MS)) {
digitalWrite(WATER_PUMP_PIN, LOW);
pumpIsOn = false;
}
}
// =======================
// MISE À JOUR DU LCD
// =======================
void updateLCD(unsigned long now) {
if (now - lastLcdMs < LCD_MS) return;
lastLcdMs = now;
lcd.clear();
if (lcdScreen == 0) {
// Écran 1 : température + humidité
if (dhtOk) {
lcd.setCursor(0, 0);
lcd.print("T:");
lcd.print(temperature, 1);
lcd.print((char)223);
lcd.print("C");
lcd.setCursor(0, 1);
lcd.print("H:");
lcd.print(humidity, 0);
lcd.print("%");
} else {
lcd.setCursor(0, 0);
lcd.print("DHT ERREUR");
}
}
else if (lcdScreen == 1) {
// Écran 2 : humidité sol + pompe
lcd.setCursor(0, 0);
lcd.print("Sol:");
lcd.print(soilPercent);
lcd.print("%");
lcd.setCursor(0, 1);
lcd.print("Pompe:");
lcd.print(pumpIsOn ? "ON" : "OFF");
}
else {
// Écran 3 : mode + IP
lcd.setCursor(0, 0);
lcd.print("Mode:");
lcd.print(modeText());
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
}
lcdScreen = (lcdScreen + 1) % 3;
}
// =======================
// ENVOI JSON
// =======================
void sendJSON(WiFiClient &c) {
c.println("HTTP/1.1 200 OK");
c.println("Content-Type: application/json");
c.println("Connection: close");
c.println();
c.print("{\"temp\":");
c.print(dhtOk ? temperature : 0);
c.print(",\"hum\":");
c.print(dhtOk ? humidity : 0);
c.print(",\"soil\":");
c.print(soilPercent);
c.print(",\"pump\":");
c.print(pumpIsOn ? "true" : "false");
c.print(",\"mode\":\"");
c.print(modeText());
c.print("\"}");
}
// =======================
// ENVOI PAGE WEB
// =======================
void sendPage(WiFiClient &c) {
c.println("HTTP/1.1 200 OK");
c.println("Content-Type: text/html; charset=utf-8");
c.println("Connection: close");
c.println();
c.println(R"rawliteral(
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>PROJET OBED MARIANE</title>
<style>
:root{
--blue:#19a7ff;
--bg:#f3f6fb;
--card:#ffffff;
--text:#1f2a37;
--muted:#6b7280;
--shadow:0 10px 30px rgba(0,0,0,.08);
--radius:14px;
}
*{box-sizing:border-box}
body{
margin:0;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
background:var(--bg);
color:var(--text);
}
.topbar{
height:64px;
background:var(--blue);
color:#fff;
display:flex;
align-items:center;
padding:0 18px;
font-weight:800;
letter-spacing:.2px;
box-shadow:0 6px 16px rgba(25,167,255,.35);
}
.wrap{display:flex; min-height:calc(100vh - 64px);}
.sidebar{
width:240px;
background:#fff;
box-shadow: 6px 0 16px rgba(0,0,0,.04);
padding:18px 14px;
}
.brand{
display:flex; gap:10px; align-items:center;
padding:6px 8px 14px;
border-bottom:1px solid #eef2f7;
margin-bottom:12px;
}
.logo{
width:38px;height:38px;border-radius:10px;
background: linear-gradient(135deg, #ffb703, #fb8500);
}
.brand b{display:block; line-height:1.1}
.brand small{color:var(--muted); font-weight:700}
.nav a{
display:flex; gap:10px; align-items:center;
padding:10px 10px;
border-radius:10px;
text-decoration:none;
color:var(--text);
font-weight:750;
}
.nav a:hover{background:#f3f6fb}
.nav .active{background:#eaf6ff; color:#0b76c9}
.icon{width:18px;height:18px;border-radius:5px;background:#e5e7eb}
.main{flex:1; padding:18px;}
.header{
display:flex; align-items:flex-end; justify-content:space-between;
gap:14px; margin-bottom:14px;
}
.header h1{margin:0;font-size:28px}
.header p{margin:4px 0 0;color:var(--muted);font-weight:650;font-size:13px}
.kpis{
display:grid;
grid-template-columns: repeat(3, 1fr);
gap:12px;
}
.kpi{
background:var(--card);
border-radius:var(--radius);
box-shadow:var(--shadow);
padding:12px;
min-height:140px;
}
.kpi .label{color:var(--muted); font-weight:750; font-size:12px; margin-top:8px}
.kpi .big{font-size:18px; font-weight:900; margin-top:2px}
.donut{
width:86px;height:86px; position:relative;
display:grid; place-items:center; margin:auto;
}
.donut svg{transform:rotate(-90deg)}
.donut .center{
position:absolute; text-align:center;
font-weight:900; font-size:14px;
}
.donut .center small{
display:block; font-weight:750; color:var(--muted); font-size:10px
}
.card{
background:var(--card);
border-radius:var(--radius);
box-shadow:var(--shadow);
padding:14px;
margin-top:14px;
}
.card h3{margin:0 0 10px;font-size:14px;color:#374151}
.row{
display:flex; align-items:center; justify-content:space-between;
padding:10px 0;
border-bottom:1px solid #eef2f7;
}
.row:last-child{border-bottom:none}
.k{color:var(--muted); font-weight:700}
.v{font-weight:900}
.controls{display:flex;gap:10px;flex-wrap:wrap;margin-top:12px}
.btn{
flex:1; min-width:120px;
padding:12px 12px;
border:none;
border-radius:12px;
font-weight:900;
font-size:14px;
cursor:pointer;
box-shadow:0 10px 22px rgba(0,0,0,.08);
}
.btn:active{transform:translateY(1px)}
.auto{background:#22c55e;color:#fff}
.on{background:#3b82f6;color:#fff}
.off{background:#ef4444;color:#fff}
.badge{
display:inline-flex; align-items:center; gap:8px;
padding:8px 10px;
border-radius:999px;
background:#fff;
box-shadow:var(--shadow);
font-weight:900;
}
.dot{width:10px;height:10px;border-radius:999px;background:#9ca3af}
.dot.on{background:#22c55e}
.dot.off{background:#ef4444}
@media (max-width: 920px){
.sidebar{display:none}
.kpis{grid-template-columns:1fr}
}
</style>
</head>
<body>
<div class="topbar">PROJET OBED MARIANE</div>
<div class="wrap">
<aside class="sidebar">
<div class="brand">
<div class="logo"></div>
<div>
<b>IRRIGATION INTELLIGENT</b>
<small>UNO R4 WiFi</small>
</div>
</div>
<nav class="nav">
<a class="active" href="#"><span class="icon"></span>Tableau</a>
</nav>
</aside>
<main class="main">
<div class="header">
<div>
<h1>Dashboard</h1>
<p>Système d'irrigation intelligent</p>
</div>
<div class="badge">
<span id="pumpDot" class="dot off"></span>
Pompe: <span id="pState">OFF</span> · Mode: <span id="mState">AUTO</span>
</div>
</div>
<section class="kpis">
<div class="kpi">
<div class="donut">
<svg width="86" height="86" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="38" stroke="#e5e7eb" stroke-width="12" fill="none"/>
<circle id="soilArc" cx="50" cy="50" r="38" stroke="#ef4444" stroke-width="12" fill="none"
stroke-linecap="round" stroke-dasharray="0 999"/>
</svg>
<div class="center"><span id="soilPct">--</span><small>Soil</small></div>
</div>
<div class="label">Humidité du sol</div>
<div class="big"><span id="sVal">--</span> %</div>
</div>
<div class="kpi">
<div class="donut">
<svg width="86" height="86" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="38" stroke="#e5e7eb" stroke-width="12" fill="none"/>
<circle id="humArc" cx="50" cy="50" r="38" stroke="#22c55e" stroke-width="12" fill="none"
stroke-linecap="round" stroke-dasharray="0 999"/>
</svg>
<div class="center"><span id="humPct">--</span><small>Hum</small></div>
</div>
<div class="label">Humidité (DHT22)</div>
<div class="big"><span id="hVal">--</span> %</div>
</div>
<div class="kpi">
<div class="donut">
<svg width="86" height="86" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="38" stroke="#e5e7eb" stroke-width="12" fill="none"/>
<circle id="tempArc" cx="50" cy="50" r="38" stroke="#3b82f6" stroke-width="12" fill="none"
stroke-linecap="round" stroke-dasharray="0 999"/>
</svg>
<div class="center"><span id="tempPct">--</span><small>Temp</small></div>
</div>
<div class="label">Température (DHT22)</div>
<div class="big"><span id="tVal">--</span> °C</div>
</div>
</section>
<div class="card">
<h3>État du système</h3>
<div class="row"><span class="k">Température</span><span class="v" id="t">--</span></div>
<div class="row"><span class="k">Humidité</span><span class="v" id="h">--</span></div>
<div class="row"><span class="k">Humidité sol</span><span class="v" id="s">--</span></div>
<div class="row"><span class="k">Pompe</span><span class="v" id="p">--</span></div>
<div class="row"><span class="k">Mode</span><span class="v" id="m">--</span></div>
<div class="controls">
<button class="btn auto" onclick="setMode('auto')">Mode AUTO</button>
<button class="btn on" onclick="setMode('on')">Pompe ON</button>
<button class="btn off" onclick="setMode('off')">Pompe OFF</button>
</div>
</div>
</main>
</div>
<script>
function setDonut(arcEl, pct, color){
pct = Math.max(0, Math.min(100, pct));
const r = 38;
const c = 2 * Math.PI * r;
arcEl.style.strokeDasharray = (c * pct / 100) + " " + c;
arcEl.style.stroke = color;
}
async function refresh(){
try{
const r = await fetch('/data', {cache:'no-store'});
const d = await r.json();
document.getElementById('t').textContent = d.temp + " °C";
document.getElementById('h').textContent = d.hum + " %";
document.getElementById('s').textContent = d.soil + " %";
document.getElementById('p').textContent = d.pump ? "ON" : "OFF";
document.getElementById('m').textContent = d.mode;
document.getElementById('pState').textContent = d.pump ? "ON" : "OFF";
document.getElementById('mState').textContent = d.mode;
document.getElementById('pumpDot').className = "dot " + (d.pump ? "on" : "off");
document.getElementById('tVal').textContent = d.temp;
document.getElementById('hVal').textContent = d.hum;
document.getElementById('sVal').textContent = d.soil;
const tempPctVal = Math.round((Math.min(50, Math.max(0, Number(d.temp))) / 50) * 100);
document.getElementById('tempPct').textContent = tempPctVal + "%";
document.getElementById('humPct').textContent = Number(d.hum) + "%";
document.getElementById('soilPct').textContent = Number(d.soil) + "%";
setDonut(document.getElementById('tempArc'), tempPctVal, "#3b82f6");
setDonut(document.getElementById('humArc'), Number(d.hum), "#22c55e");
const soilVal = Number(d.soil);
const soilColor = (soilVal < 30) ? "#ef4444" : (soilVal < 60) ? "#f59e0b" : "#22c55e";
setDonut(document.getElementById('soilArc'), soilVal, soilColor);
} catch(e) {}
}
async function setMode(mode){
try{
await fetch('/pump?mode=' + encodeURIComponent(mode), {cache:'no-store'});
} catch(e) {}
refresh();
}
setInterval(refresh, 2000);
refresh();
</script>
</body>
</html>
)rawliteral");
}
// =======================
// INITIALISATION
// =======================
void setup() {
Serial.begin(9600);
dht.begin();
// Entête CSV pour Excel / Data Streamer
Serial.println("Humidity,TempC,SoilPercent,Pump,Mode");
pinMode(WATER_PUMP_PIN, OUTPUT);
digitalWrite(WATER_PUMP_PIN, LOW);
lcd.init();
lcd.backlight();
lcdConnect();
while (status != WL_CONNECTED) {
status = WiFi.begin(ssid, pass);
delay(8000);
}
server.begin();
lcdIP();
delay(2000);
// Lecture initiale
readSensors();
}
// =======================
// BOUCLE PRINCIPALE
// =======================
void loop() {
unsigned long now = millis();
// Lecture périodique des capteurs + envoi CSV pour Excel
if (now - lastSensorMs >= SENSOR_MS) {
lastSensorMs = now;
readSensors();
if (dhtOk) {
Serial.print(humidity, 1);
Serial.print(",");
Serial.print(temperature, 1);
Serial.print(",");
Serial.print(soilPercent);
Serial.print(",");
Serial.print(",");
} else {
Serial.println("ERROR,ERROR,ERROR,ERROR,ERROR");
}
}
handlePump(now);
updateLCD(now);
WiFiClient c = server.available();
if (!c) return;
String req = c.readStringUntil('\r');
c.readStringUntil('\n');
while (c.available()) c.read();
if (req.indexOf("/data") >= 0) {
sendJSON(c);
}
else if (req.indexOf("/pump?mode=auto") >= 0) {
pumpMode = AUTO;
sendJSON(c);
}
else if (req.indexOf("/pump?mode=on") >= 0) {
pumpMode = MANUAL_ON;
sendJSON(c);
}
else if (req.indexOf("/pump?mode=off") >= 0) {
pumpMode = MANUAL_OFF;
sendJSON(c);
}
else {
sendPage(c);
}
c.stop();
}
