ArduFarmBot Partie 2 : Construction d'une Station de Contrôle IoT à Distance
Composants et Fournitures
| Composant | Description | Quantité |
|---|---|---|
| Arduino Nano R3 | Microcontrôleur principal | 1 |
| Espressif ESP8266 ESP-01 | Module WiFi pour connectivité IoT | 1 |
| Capteur Température/Humidité DHT22 | Mesure air ambiant | 1 |
| Module Photoreflecteur KY-018 | Capteur luminosité | 1 |
| Capteur Humidité Sol (Hydrometer) | Mesure humidité du sol | 1 |
| Relais (générique) | Contrôle pompe et lampe | 2 |
| Écran LCD 4x20 (I2C) | Affichage données | 1 |
| Bouton Poussoir 12mm SparkFun | Clavier membrane 4 touches | 3 |
| LED (générique) | Indicateurs d'état | 3 |
Applications et Services en Ligne
| Service | Description |
|---|---|
| ThingSpeak API | Plateforme cloud pour stockage et visualisation données IoT |
À Propos de ce Projet



Ce tutoriel est la continuation de ArduFarmBot : Contrôle d'une Ferme Tomate Maison avec Arduino et IoT.
Dans la première partie, nous avons créé une station de contrôle locale, capturant les informations d'une plantation de tomates : température, humidité relative de l'air, luminosité et humidité du sol. Sur la base de ces données, l'ArduFarmBot décidait automatiquement la bonne quantité (et le moment) d'arrosage et de chauffage. La station locale de la Partie 1 permettait aussi une intervention manuelle d'un opérateur pour contrôler la pompe à eau et la lampe électrique. Dans cette Partie 2, nous implémentons une approche IoT où cette "intervention manuelle" sera également possible à distance via Internet. Le diagramme de blocs montre comment nous procéderons.
Notez que les données capturées seront envoyées vers un "service de stockage Cloud" (dans notre cas ThinkSpeak.com). Un site web dédié, la "Page de Contrôle à Distance", surveillera et affichera ces données en quasi temps réel. Cette page web permettra aussi l'activation à distance de la pompe et de la lampe.
Étape 1 : Liste du Matériel (Bill of Material)
- Arduino Nano - (7,50 $)
- Capteur Température/Humidité DHT22 ou DHT11 - (3,66 $)
- Capteur Luminosité - Module photorésistance AD-018 ou équivalent - (0,71 $)
- 2× Capteur Humidité Sol - (1,99 $) (Optionnel, peut être DIY)
- Écran LCD I2C 20x4 - (13,99 $)
- LEDs (1×) - (0,20 $)
- Module WiFi ESP8266 ESP-01 - (5,96 $)
- Buzzer Actif - KY-12 ou équivalent - (0,60 $)
- 2 × Module Relais 5V - (11,60 $)
- Fils de liaison - (1,00 $)
- Résistances : 10kΩ, 2,2kΩ, 1kΩ, 220Ω - (0,03 $ chacune)
- Shield Arduino Nano ("Funduino") - (7,28 $)
- Clavier à membrane (4 touches) - (6,65 $)
- Boîte en plastique
Étape 2 : Compléter le Matériel







En partant de la Station Locale développée dans la Partie 1, le seul matériel supplémentaire nécessaire est l'ESP8266. Le diagramme de blocs ci-dessus montre toutes les connexions des broches Arduino et composants principaux. La seule précaution concerne le niveau de tension : l'ESP8266 fonctionne en 3,3V, donc la broche Rx ne doit pas être connectée directement à la broche Tx du Nano (D3). Un diviseur de tension doit être utilisé comme convertisseur de niveau. Les schémas ci-dessus montrent en détail comment connecter l'ESP8266. Pour en savoir plus sur l'ESP8266, consultez mes tutoriels :
- ESP8266 Partie 3 - Déclencher des LEDs Arduino à Distance
- ESP8266 Partie 1 - Module WiFi Série pour Arduino
- ESP8266 Partie 2 - Serveur Web Arduino
Note : nous utilisons l'ESP8266 connecté aux broches Nano 2 (Tx) et 3 (Rx) via la bibliothèque SoftSerial. Pour "libérer" ces broches numériques, vous pouvez alternativement utiliser les broches série natives 0 et 1 du Nano. N'oubliez pas de les déconnecter lors du téléversement du code.
NOTE : Si vous voulez connecter le BUZZER, faites-le sur la broche D17 (identique à A3). C'est bien d'avoir un son en cas d'erreur de communication. Je l'ai utilisé pendant la phase de test, puis retiré au projet final (le matériel, mais le code le prévoit). À vous de voir.
Vous pouvez utiliser le code ci-dessous pour tester et/ou configurer votre ESP8266 :
FC9DBPKIT682FY7.ino
Étape 3 : Connexion de l'ESP8266

Pour connecter l'ArduFarmBot à Internet, nous utilisons l'ESP8266, un module simple, bon marché et facile à programmer pour projets IoT. Une fois le module installé, la première chose à faire est d'appliquer un "Reset" sur sa broche CH-PD :
/***************************************************
* Fonction Reset pour accepter la communication
****************************************************/
void reset8266(void) {
pinMode(CH_PD, OUTPUT);
digitalWrite(CH_PD, LOW);
delay(300);
digitalWrite(CH_PD, HIGH);
Serial.print("8266 reset OK");
lcd.clear();
lcd.println("8266 reset OK ");
}
Après le reset, connectons-le à votre réseau local avec vos identifiants (dans le code, remplacez : USERNAME et PASSWORD) et initialisons le module en mode "STA: Station Mode" (CWMODE = 1) :
/***************************************************
* Connexion WiFi
****************************************************/
void connectWiFi(void) {
sendData("AT+RST\r\n", 2000, DEBUG); // reset
sendData("AT+CWJAP=\"USERNAME\",\"PASSWORD\"\r\n", 2000, DEBUG); // Connexion réseau
delay(3000);
sendData("AT+CWMODE=1\r\n", 1000, DEBUG);
sendData("AT+CIFSR\r\n", 1000, DEBUG); // Afficher adresse IP
lcd.clear();
lcd.print("8266 Connected");
Serial.println("8266 Connected");
}
Pour envoyer des données à l'ESP8266, on utilise la fonction sendData() :
/***************************************************
* Envoi commandes AT au module
****************************************************/
String sendData(String command, const int timeout, boolean debug) {
String response = "";
esp8266.print(command);
long int time = millis();
while ( (time + timeout) > millis()) {
while (esp8266.available()) {
char c = esp8266.read();
response += c;
}
}
if (debug) {
Serial.print(response);
}
return response;
}
Ces fonctions seront appelées pendant la "Phase Setup" de notre code. Si tout est correct, vous devriez voir des messages similaires au Moniteur Série.
Étape 4 : Stockage Cloud des Données : ThinkSpeak.com

Toutes les données capturées par l'ArduFarmBot seront envoyées dans le cloud via le service gratuit de ThinkSpeak.com.
Dans la fonction Loop() (après avoir capturé les données avec readSensors()), nous appellerons une fonction spécifique pour envoyer les données : updateDataThingSpeak();
/***************************************************
* Transmission données vers thingspeak.com
****************************************************/
void updateDataThingSpeak(void) {
startThingSpeakCmd ();
cmd = msg ;
cmd += "&field1="; // field 1 pour température DHT
cmd += tempDHT;
cmd += "&field2="; // field 2 pour humidité DHT
cmd += humDHT;
cmd += "&field3="; // field 3 pour luminosité LDR
cmd += lumen;
cmd += "&field4="; // field 4 pour humidité sol
cmd += soilMoist;
cmd += "&field5="; // field 5 pour statut POMPE
cmd += pumpStatus;
cmd += "&field6="; // field 6 pour statut LAMPE
cmd += lampStatus;
cmd += "\r\n";
sendThingSpeakCmd();
}
Pour envoyer ces données, il faut d'abord démarrer la communication avec ThingSpeak via la fonction startThingSpeakCmd (); :
/***************************************************
* Démarrage communication ThingSpeak.com
****************************************************/
void startThingSpeakCmd(void) {
cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += IP;
cmd += "\",80";
esp8266.println(cmd);
delay(2000);
if(esp8266.find("Error")) {
Serial.println("ESP8266 START ERROR");
return;
}
Serial.println("Thinkspeak Comm Started");
cmd ="";
}
Une fois le canal ouvert avec ThingSpeak et la chaîne "cmd" assemblée avec les données, il est temps de tout envoyer sur le canal correspondant via la fonction sendThingSpeakCmd(); :
/***************************************************
* Mise à jour canal ThingSpeak.com
****************************************************/
String sendThingSpeakCmd(void) {
esp8266.print("AT+CIPSEND=");
esp8266.println(cmd.length());
if(esp8266.find(">")){
esp8266.print(cmd);
Serial.println("");
Serial.println("");
Serial.println(cmd);
delay(500);
String messageBody = "";
while (esp8266.available()) {
String line = esp8266.readStringUntil('\n');
if (line.length() == 1) {
// contenu réel commence après ligne vide (longueur 1)
messageBody = esp8266.readStringUntil('\n');
Serial.print("Message received: ");
Serial.println(messageBody);
}
}
return messageBody;
} else{
esp8266.println("AT+CIPCLOSE");
Serial.println("ESP8266 CIPSEND ERROR: RESENDING");
// Renvoi...
error=1;
return "error";
}
}
Ces fonctions sont basées sur un excellent tutoriel détaillé de Michalis Vasilakis. Pour plus de détails, voir son tutoriel : Arduino IOT: Temperature and Humidity (with ESP8266 WiFi).
Étape 5 : Commander les Actionneurs depuis le Web


À ce stade, nous envoyons toutes les données collectées et les stockons dans le cloud. C'est très bien pour la surveillance à distance, mais que se passe-t-il si, basé sur ces données, nous voulons aussi allumer la Pompe ou la Lampe, indépendamment du programme automatique local ? Pour cela, nous devons aussi "Télécharger" des données du Cloud et commander le contrôleur d'agir selon ces commandes.
Nous créerons des champs spécifiques sur notre canal ThinkSpeak pour commander les actionneurs :
Champ 7 :
- Donnée = 1 ⇒ POMPE doit être ALLUMÉE
- Donnée = 0 ⇒ POMPE doit être ÉTEINTE
Champ 8 :
- Donnée = 1 ⇒ LAMPE doit être ALLUMÉE
- Donnée = 0 ⇒ LAMPE doit être ÉTEINTE
Comment configurer ces champs directement sur ThingSpeak ? On peut le faire en écrivant un "PlugIn" directement sur ThinkSpeak, ou en utilisant un site web externe (notre choix). Dans les deux cas, on utilise une commande comme :
api.thingspeak.com/update?key=YOUR_WRITE_KEY&field7=1
Avec cette commande (et votre clé d'écriture), vous écrirez "1" au champ 7, ce qui signifie que la POMPE doit s'allumer. Testez facilement en entrant la commande dans votre navigateur – le champ correspondant de votre canal changera. En retour, le navigateur affiche une page blanche avec un seul chiffre en haut à gauche, correspondant à l'entrée séquentielle dans votre canal.
50 % du travail est fait. Maintenant, il faut lire cette "commande" (donnée du champ) sur la station ArduFarmBot locale.
La commande pour lire la dernière donnée écrite sur un champ spécifique :
api.thingspeak.com/channels/CHANNEL_ID/fields/7/last
Même principe : testez dans votre navigateur. Il affichera la donnée de ce champ. Voir la photo ci-dessus.
Retour sur terre, écrivons une fonction qui lit ce "dernier champ" :
/***************************************************
* Lecture données field7 de thingspeak.com
****************************************************/
int readLastDataField7() {
startThingSpeakCmd ();
// "GET /channels/CHANNEL_ID/fields/7/last";
cmd = msgReadLastDataField7;
cmd += "\r\n";
String messageDown = sendThingSpeakCmd();
Serial.print("Command received: ");
Serial.println(messageDown[7]);
int command = messageDown[7]-48;
return command;
}
Cette fonction retourne la donnée du champ 7 ("1" ou "0"). Une fonction similaire pour le Champ 8.
Une fois le contenu des deux champs récupéré, on l'utilise dans une fonction qui commande les actionneurs comme on le faisait pour la "fonction commande manuelle" :
/***************************************************
* Réception Commandes depuis thingSpeak.com
****************************************************/
void receiveCommands() {
field7Data = readLastDataField7();
if (field7Data == 1) {
digitalWrite(PUMP_PIN, HIGH);
pumpStatus = 1;
showDataLCD();
}
if (field7Data == 0) {
digitalWrite(PUMP_PIN, LOW);
pumpStatus = 0;
showDataLCD();
}
delay (500);
field8Data = readLastDataField8();
if (field8Data == 1) {
digitalWrite(LAMP_PIN, HIGH);
lampStatus = 1;
showDataLCD();
}
if (field8Data == 0) {
digitalWrite(LAMP_PIN, LOW);
lampStatus = 0;
showDataLCD();
}
delay (500);
}
Dès maintenant, vous pouvez utiliser la ligne de commande dans votre navigateur pour Allumer/Éteindre la Pompe et la Lampe à distance. La photo ci-dessus montre comment la commande reçue apparaît sur votre Moniteur Série.
Autre point important : la "coordination" entre commande locale et distante. Nous devons modifier la fonction readLocalCmd() pour aussi mettre à jour les champs ThingSpeak 7 et 8 respectivement avec le statut Pompe et Lampe (dans leur "IF statement" respectif – voir le code complet à la fin) :
field7Data = pumpStatus;
field8Data = lampStatus;
Maintenant "field7Data" et "field8Data" sont synchronisés avec les commandes de la page web et aussi avec les actions locales quand vous appuyez sur un bouton. Mettons à jour la fonction aplyCmd(), responsable d'allumer/éteindre les actionneurs :
/***************************************************
* Réception Commandes et action sur actionneurs
****************************************************/
void aplyCmd() {
if (field7Data == 1) digitalWrite(PUMP_PIN, HIGH);
if (field7Data == 0) digitalWrite(PUMP_PIN, LOW);
if (field8Data == 1) digitalWrite(LAMP_PIN, HIGH);
if (field8Data == 0) digitalWrite(LAMP_PIN, LOW);
}
Quand vous commencez vos tests, vous verrez que toute commande manuelle (locale ou web) sera écrasée par les actions automatiques définies par autoControlPlantation(). À ce stade, il faut décider qui sera le "chef", ayant le dernier mot ! Dans notre cas :
- À chaque cycle de boucle (quasiment "toujours"), on vérifie si un bouton est pressé
- Toutes les minutes environ, on fait un "polling" sur ThingSpeak pour voir si on a reçu un ordre
- Toutes les 10 minutes environ, on lit les capteurs, met à jour les données sur ThingSpeak et, plus important, on prend les actions automatiques. Ces actions seront prises indépendamment de ce qui a été sélectionné manuellement et seront celles qui seront maintenues.
Vous pouvez changer cette logique comme vous voulez. C'est l'avantage d'utiliser un processeur programmable !
Deux temporisateurs seront utilisés : un pour le polling des commandes distantes, un autre pour la lecture des capteurs (le même qu'avant) :
long sampleTimingSeconds = 75; // Temps échantillonnage en secondes pour lecture capteurs
int reverseElapsedTimeSeconds = 0;
long startTiming = 0;
long elapsedTime = 0;
long poolingRemoteCmdSeconds = 20; // Temps polling en secondes pour nouvelles commandes ThingSpeak
long startRemoteCmdTiming = 0;
long elapsedRemoteCmdTime = 0;
La fonction loop() doit maintenant être réécrite ainsi :
void loop() {
elapsedRemoteCmdTime = millis()-startRemoteCmdTiming; // Timer polling commandes distantes
elapsedTime = millis()-startTiming; // Timer mesures
reverseElapsedTimeSeconds = round (sampleTimingSeconds - elapsedTime/1000);
readLocalCmd(); // Lecture statut boutons locaux
showDataLCD();
if (elapsedRemoteCmdTime > (poolingRemoteCmdSeconds*1000)) {
receiveCommands();
updateDataThingSpeak();
startRemoteCmdTiming = millis();
}
if (elapsedTime > (sampleTimingSeconds*1000)) {
readSensors();
autoControlPlantation();
updateDataThingSpeak();
startTiming = millis();
}
}
Étape 6 : Implémentation d'une Page Web Dédiée

L'ArduFarmBot est opérationnel et contrôlable depuis le web. Vous pouvez surveiller les données sur ThingSpeak et envoyer des commandes via navigateur, mais ce n'est pas une solution "élégante" ! La meilleure façon d'implémenter une solution IoT complète est de développer une page web complète qui affiche toutes les données et propose des boutons pour actionner les actionneurs.
J'ai choisi Byethost, un hébergeur web gratuit, très simple pour gérer vos pages. J'ai aussi profité de l'occasion pour appliquer ce que j'ai appris dans une excellente spécialisation en ligne sur Coursera/Université du Michigan : Learn to Design and Create Websites (Construire un portfolio web responsive et accessible avec HTML5, CSS3 et JavaScript).
Je ne détaillerai pas le développement de cette page (ce n'est pas le but de ce tutoriel), mais j'inclus ici les codes sources HTML, CSS et JavaScript. Si quelqu'un s'intéresse à la méthode, on peut en discuter dans les commentaires.
Il est important de noter que cette page n'interagit pas directement avec la Station de Contrôle Locale ArduFarmBot. Elle interagit avec le canal ThingSpeak comme suit :
- Lecture données capteurs sur champs 1, 2, 3, 4
- Lecture statut actionneurs sur champs 5 et 6
- Écriture données sur champs 7 et 8
- Lecture météo locale via services Yahoo
Le point 4 n'est pas crucial mais fournit des données additionnelles pour d'éventuelles actions à distance indépendantes de la plantation locale. Vous pourriez par exemple stocker ces données sur un autre canal ThingSpeak et les télécharger vers votre Arduino pour afficher la météo sur l'écran LCD local (j'ai fait ça sur un autre projet sympa qui marchait bien ! Je le laisse comme suggestion).
FQOK9ENIT653YX5.zip
Étape 7 : Retour au Cerveau. Approche Matrice Capteur-Actionneur



Dans la première partie, nous avions défini des considérations préliminaires sur comment les actionneurs devaient agir selon les lectures des capteurs. Nous n'avions fait que des choix simples, mais que se passe-t-il dans des situations plus complexes ? Plusieurs conditions différentes ? Nous allons développer une "approche Matrice Capteur-Actionneur".
Dans une Matrice, on définit pour chaque capteur sa condition et comment doit être la sortie des actionneurs. Le résultat est visible dans le fichier Excel joint ci-dessous. Il y a deux onglets : un tableau avec filtre et une version où vous pouvez sélectionner plusieurs conditions de capteurs et voir comment les actionneurs réagiront.
Une fois la Matrice définie, il faut la traduire en code. Un tableau de 18 lignes et 10 colonnes a été créé pour "copier" les conditions de la Matrice Capteur-Actionneur :
// +---SOIL----+-LIGHT-+---TEMP---+---ACTUAT----+
// SL SM SH LL LH TL TM TH Pump Lamp
boolean SDf [18] [10] = {
{ 1, 0, 0, 0, 1, 0, 0, 1, 1, 0 },
{ 1, 0, 0, 0, 1, 0, 1, 0, 1, 0 },
{ 1, 0, 0, 0, 1, 1, 0, 0, 1, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 1, 1, 0 },
{ 1, 0, 0, 1, 0, 0, 1, 0, 1, 0 },
{ 1, 0, 0, 1, 0, 1, 0, 0, 0, 1 },
{ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 },
{ 0, 1, 0, 0, 1, 1, 0, 0, 0, 1 },
{ 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 },
{ 0, 1, 0, 1, 0, 0, 1, 0, 0, 1 },
{ 0, 1, 0, 1, 0, 1, 0, 0, 0, 1 },
{ 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 },
{ 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 0, 1, 1, 0, 0, 0, 1 },
{ 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 0, 0, 1 },
{ 0, 0, 1, 1, 0, 1, 0, 0, 0, 1 },
};
Pour travailler avec la Matrice, nous créons une fonction defSensorStatus (). Cette fonction teste pour chaque ligne si la condition des 8 premières colonnes est VRAIE. Si oui, la condition des 2 dernières colonnes est exécutée.
Par exemple :
if (1 and 0 and 0 and 0 and 1 and 0 and 0 and 1) {
pumpStatus = 1; lampStatus = 0
} else if (1 and 0 and 0 and 0 and 1 and 0 and 1 and 0) {
pumpStatus = 1; lampStatus = 0
}
Et ainsi de suite.
À l'intérieur de cette fonction, un autre tableau est créé avec le statut de chaque lecture de capteur :
boolean snsSts[8]={0, 0, 0, 0, 0, 0, 0, 0}; // SL, SM, SH, LL, LH, TL, TM, TH
Ce tableau servira aussi pour le registre LOG.
F2HWXBYITA8WIN1.xlsx
Étape 8 : Optimisation du Code


Pendant le développement de l'ArduFarmBot, nous avons réalisé que certains changements par rapport à la spécification originale étaient nécessaires :
Affichage :
L'écran LCD doit être ÉTEINT par défaut. À chaque fois qu'une lecture de capteurs est nécessaire, on peut l'allumer manuellement. Cette condition a été implémentée dans le code : le bouton "Lecture Capteurs" fonctionne en mode "toggle" pour Allumer/Éteindre le LCD à tout moment. Allumer ou éteindre l'affichage mettra à jour les lectures pour l'affichage, mais ne sera pas utilisé par l'ArduFarmBot dans ses fonctions régulières.
Configuration Initiale :
Au démarrage (ou reset) de l'ArduFarmBot, le LCD affiche la "Configuration Initiale". Pour lancer le programme, il faut appuyer sur le bouton "Capteurs". Les informations initiales affichées (voir photo) :
- Température FROIDE (ex: 12°C)
- Humidité Sol SÈCHE (ex: 30%)
- Humidité Sol HUMIDE (ex: 60%)
- Luminosité SOMBRE (ex: 40%)
- P_ON Temps pompe ON (ex: 10s)
- SCAN Temps lecture capteurs (ex: 600s)
- SW_Version (ex: 4.1)
Registre LOG :
Pour des raisons d'audit, nous avons créé un LOG avec les lectures et actions de notre ArduFarmBot. À chaque cycle de lecture, la fonction storeDataLogEEPROM() est exécutée.
/***************************************************
* Stockage données Log dans EEPROM Arduino
****************************************************/
void storeDataLogEEPROM(void) {
for (int i = 0; i<8; i++) {
logData = logData + (snsSts[i])<< 1;
}
EEPROM.write (memoAddr, logData);
memoAddr++;
logData = 0;
logData = logData + pumpStatus;
logData = logData << 1;
logData = logData + lampStatus;
EEPROM.write (memoAddr, logData);
EEPROM.write (0, memoAddr+1);
logData = 0;
if ((memoAddr+1) == 1023) memoAddr=1;
else memoAddr++;
}
Comme mentionné à l'étape précédente, ce qui est stocké dans l'EEPROM Arduino est le contenu, bit par bit, du tableau snsSts[] plus le statut Pompe et Lampe. Vous pouvez voir le LOG sur le Moniteur Série.
Tout le code ArduFarmBot a été divisé en différents fichiers pour être plus facile à comprendre. Notez que 2 nouveaux fichiers ont été ajoutés dans cette deuxième partie :
- communication.ino (fonctions spécifiques ThingSpeak et ESP8266)
- stationCredentials.h (ID Canal ThingSpeak et Clés spécifiques pour écriture sur le canal)
Enfin, une fois le code arrivé à une taille raisonnable, nous avons décidé de stocker les données constantes en mémoire flash (programme) au lieu de la SRAM. Pour cela, on utilise le mot-clé PROGMEM qui est un modificateur de variable. Par exemple, au lieu de :
#define DHTPIN 5
Nous avons utilisé :
const PROGMEM byte DHTPIN = 5;
Le mot-clé PROGMEM dit au compilateur "mets cette info en mémoire flash", au lieu de la SRAM où elle irait normalement. Vous devez aussi inclure la bibliothèque avr/pgmspace.h dans le fichier principal.
Une autre bonne pratique pour réduire l'usage de la SRAM : commenter (ou supprimer) toutes les lignes Serial.Print() utilisées pour le débogage pendant le développement. Vous verrez que le code pour afficher le LOG sur le Moniteur Série sera commenté dans les fichiers ci-dessous.
Ci-dessous, le code complet de l'ArduFarmBot Arduino. N'oubliez pas de changer les données factices dans credentials.h avec votre ID Canal et Clé d'écriture. Aussi dans communication.ino, utilisez vos vrais identifiants pour connecter l'ESP8266 à Internet.
FTUT5VIIT67U8ME.ino
FWMIPSSIT67U8MG.ino
FJPGZNKIT67U8MK.ino
FQH3X9VIT67U8NA.ino
F15MY4YIT67U8NB.ino
FVU64X1IT67U8NC.h
FZ057E3IT67U8P5.h
Étape 9 : La Ferme Maison MJRovai
Les photos ci-dessous montrent l'évolution séquentielle de l'ArduFarmBot contrôlant ma première plantation de tomates.











La séquence photo ci-dessous montre l'évolution de ma 2e plantation, du semis jusqu'à la sélection des meilleurs plants (environ 45 jours) et la transplantation des 6 meilleurs.


Étape 10 : Conclusion


C'est tout pour l'instant !
Comme toujours, j'espère que ce projet aidera d'autres à trouver leur voie dans le monde passionnant de l'électronique, de l'IoT et de la robotique !
Bientôt, nous publierons probablement la troisième et dernière partie de notre projet qui, je l'espère, sera une excellente recette de pâtes à la sauce tomate bio.
Au passage, sur la photo ci-dessus, on voit les premiers signes de vie sur la plantation de Mauricio ! Et avant de partir, jetez un œil au nouveau ArduFarmBot, le livre !, où je regroupe tout le projet dans un format plus convivial :
ArduFarmBot, le Livre !

"ArduFarmBot, le livre" est aussi sur Amazon.com ! Vous pouvez l'obtenir en cliquant ici. Le livre utilise le contrôleur électronique ArduFarmBot comme base pour enseigner comment travailler à la fois en HW et SW avec :
- Écrans LCD et OLED ;
- LEDs et boutons ;
- Activation de pompes et lampes via relais ;
- Capteurs : DHT22 (température et humidité relative air), DS18B20 (température sol), YL69 (humidité sol) et LDR (luminosité).

Toutes les étapes clés du projet sont documentées en détail via textes explicatifs, diagrammes de blocs, photos haute résolution en couleur, schémas électriques avec Fritzing, codes complets sur GitHub et vidéos YouTube.

Deux versions du contrôleur électronique ArduFarmBot sont développées en détail. À partir de la capture de données d'un jardin (température air/sol, humidité relative, humidité sol, luminosité), l'ArduFarmBot aide à contrôler quand une culture doit recevoir chaleur et eau. Le contrôle se fait automatiquement, localement et à distance via Internet. Le livre est en 3 parties. Dans la première, l'Arduino Nano est le point de départ pour développer une version locale de l'ArduFarmBot, contrôlable manuellement et automatiquement.

Dans la deuxième partie, le livre plonge dans la conception d'automatisation, introduisant le contrôle à distance via la création d'une page web. L'ESP8266-01 est utilisé pour la connexion Wi-Fi, l'envoi de données vers un service web majeur de l'IoT, ThingSpeak.com.

Dans la troisième partie, une deuxième version de l'ArduFarmBot est développée, introduisant le NodeMCU ESP8266-12E, un dispositif IoT puissant et polyvalent, qui remplace à la fois l'Arduino Nano et l'ESP8266-01 utilisés dans les parties précédentes.

Dans cette dernière partie, une nouvelle plateforme de service de l'univers IoT, Blynk, est aussi explorée.

Téléchargez le livre, donnez un avis et utilisez le forum ici pour tout commentaire, suggestion ou critique !
Pour plus de projets, visitez mon blog : MJRoBot.org
Saludos du sud du monde !
À mon prochain projet !
Merci
Marcelo
Code (Extraits)
Le code complet est divisé en plusieurs fichiers. Voici les extraits principaux :
Extrait #1 : Reset ESP8266
/***************************************************
* Fonction Reset pour accepter la communication
****************************************************/
void reset8266(void) {
pinMode(CH_PD, OUTPUT);
digitalWrite(CH_PD, LOW);
delay(300);
digitalWrite(CH_PD, HIGH);
Serial.print("8266 reset OK");
lcd.clear();
lcd.println("8266 reset OK ");
}
Extrait #2 : Connexion WiFi
/***************************************************
* Connexion WiFi
****************************************************/
void connectWiFi(void) {
sendData("AT+RST\r\n", 2000, DEBUG); // reset
sendData("AT+CWJAP=\"USERNAME\",\"PASSWORD\"\r\n", 2000, DEBUG); //Connect network
delay(3000);
sendData("AT+CWMODE=1\r\n", 1000, DEBUG);
sendData("AT+CIFSR\r\n", 1000, DEBUG); // Show IP Adress
lcd.clear();
lcd.print("8266 Connected");
Serial.println("8266 Connected");
}
Extrait #3 : Envoi Commandes AT
/***************************************************
* Envoi commandes AT au module
****************************************************/
String sendData(String command, const int timeout, boolean debug) {
String response = "";
esp8266.print(command);
long int time = millis();
while ( (time + timeout) > millis()) {
while (esp8266.available()) {
char c = esp8266.read();
response += c;
}
}
if (debug) {
Serial.print(response);
}
return response;
}
Extrait #4 : Envoi Données ThingSpeak
/***************************************************
* Transmission données vers thingspeak.com
****************************************************/
void updateDataThingSpeak(void) {
startThingSpeakCmd ();
cmd = msg ;
cmd += "&field1="; //field 1 for DHT temperature
cmd += tempDHT;
cmd += "&field2="; //field 2 for DHT humidity
cmd += humDHT;
cmd += "&field3="; //field 3 for LDR luminosity
cmd += lumen;
cmd += "&field4="; //field 4 for Soil Moisture data
cmd += soilMoist;
cmd += "&field5="; //field 5 for PUMP Status
cmd += pumpStatus;
cmd += "&field6="; //field 6 for LAMP Status
cmd += lampStatus;
cmd += "\r\n";
sendThingSpeakCmd();
}
Extrait #5 : Démarrage Communication ThingSpeak
/***************************************************
* Start communication with ThingSpeak.com
****************************************************/
void startThingSpeakCmd(void) {
cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += IP;
cmd += "\",80";
esp8266.println(cmd);
delay(2000);
if(esp8266.find("Error")) {
Serial.println("ESP8266 START ERROR");
return;
}
Serial.println("Thinkspeak Comm Started");
cmd ="";
}
Extrait #6 : Mise à jour Canal ThingSpeak
/***************************************************
* Update channel ThingSpeak.com
****************************************************/
String sendThingSpeakCmd(void) {
esp8266.print("AT+CIPSEND=");
esp8266.println(cmd.length());
if(esp8266.find(">")){
esp8266.print(cmd);
Serial.println("");
Serial.println("");
Serial.println(cmd);
delay(500);
String messageBody = "";
while (esp8266.available()) {
String line = esp8266.readStringUntil('\n');
if (line.length() == 1) {
messageBody = esp8266.readStringUntil('\n');
Serial.print("Message received: ");
Serial.println(messageBody);
}
}
return messageBody;
} else{
esp8266.println("AT+CIPCLOSE");
Serial.println("ESP8266 CIPSEND ERROR: RESENDING");
error=1;
return "error";
}
}
Extrait #9 : Lecture Champ 7 ThingSpeak
/***************************************************
* Read data from field7 of thingspeak.com
****************************************************/
int readLastDataField7() {
startThingSpeakCmd ();
// "GET /channels/CHANNEL_ID/fields/7/last";
cmd = msgReadLastDataField7;
cmd += "\r\n";
String messageDown = sendThingSpeakCmd();
Serial.print("Command received: ");
Serial.println(messageDown[7]);
int command = messageDown[7]-48;
return command;
}
Extrait #10 : Réception Commandes ThingSpeak
/***************************************************
* Receive Commands from thingSpeak.com
****************************************************/
void receiveCommands() {
field7Data = readLastDataField7();
if (field7Data == 1) {
digitalWrite(PUMP_PIN, HIGH);
pumpStatus = 1;
showDataLCD();
}
if (field7Data == 0) {
digitalWrite(PUMP_PIN, LOW);
pumpStatus = 0;
showDataLCD();
}
delay (500);
field8Data = readLastDataField8();
if (field8Data == 1) {
digitalWrite(LAMP_PIN, HIGH);
lampStatus = 1;
showDataLCD();
}
if (field8Data == 0) {
digitalWrite(LAMP_PIN, LOW);
lampStatus = 0;
showDataLCD();
}
delay (500);
}
Extrait #11 : Application Commandes Actionneurs
/***************************************************
* Receive Commands and act on actuators
****************************************************/
void aplyCmd() {
if (field7Data == 1) digitalWrite(PUMP_PIN, HIGH);
if (field7Data == 0) digitalWrite(PUMP_PIN, LOW);
if (field8Data == 1) digitalWrite(LAMP_PIN, HIGH);
if (field8Data == 0) digitalWrite(LAMP_PIN, LOW);
}
Extrait #12 : Configuration Temporisateurs
long sampleTimingSeconds = 75; // Sample time in seconds to read sensors
int reverseElapsedTimeSeconds = 0;
long startTiming = 0;
long elapsedTime = 0;
long poolingRemoteCmdSeconds = 20; // Pooling time in seconds for new ThingSpeak commands
long startRemoteCmdTiming = 0;
long elapsedRemoteCmdTime = 0;
Extrait #13 : Boucle Principale loop()
void loop() {
elapsedRemoteCmdTime = millis()-startRemoteCmdTiming; // Start timer for pooling remote commands
elapsedTime = millis()-startTiming; // Start timer for measurements
reverseElapsedTimeSeconds = round (sampleTimingSeconds - elapsedTime/1000);
readLocalCmd(); //Read local button status
showDataLCD();
if (elapsedRemoteCmdTime > (poolingRemoteCmdSeconds*1000)) {
receiveCommands();
updateDataThingSpeak();
startRemoteCmdTiming = millis();
}
if (elapsedTime > (sampleTimingSeconds*1000)) {
readSensors();
autoControlPlantation();
updateDataThingSpeak();
startTiming = millis();
}
}
Extrait #14 : Matrice Capteur-Actionneur
// +---SOIL----+-LIGHT-+---TEMP---+---ACTUAT----+
// SL SM SH LL LH TL TM TH Pump Lamp
boolean SDf [18] [10] = {
{ 1, 0, 0, 0, 1, 0, 0, 1, 1, 0 },
{ 1, 0, 0, 0, 1, 0, 1, 0, 1, 0 },
{ 1, 0, 0, 0, 1, 1, 0, 0, 1, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 1, 1, 0 },
{ 1, 0, 0, 1, 0, 0, 1, 0, 1, 0 },
{ 1, 0, 0, 1, 0, 1, 0, 0, 0, 1 },
{ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 },
{ 0, 1, 0, 0, 1, 1, 0, 0, 0, 1 },
{ 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 },
{ 0, 1, 0, 1, 0, 0, 1, 0, 0, 1 },
{ 0, 1, 0, 1, 0, 1, 0, 0, 0, 1 },
{ 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 },
{ 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 0, 1, 1, 0, 0, 0, 1 },
{ 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 0, 0, 1 },
{ 0, 0, 1, 1, 0, 1, 0, 0, 0, 1 },
};
Extrait #15 : Stockage LOG EEPROM
/***************************************************
* Storage of Log data at Arduino EEPROM
****************************************************/
void storeDataLogEEPROM(void) {
for (int i = 0; i<8; i++) {
logData = logData + (snsSts[i])<< 1;
}
EEPROM.write (memoAddr, logData);
memoAddr++;
logData = 0;
logData = logData + pumpStatus;
logData = logData << 1;
logData = logData + lampStatus;
EEPROM.write (memoAddr, logData);
EEPROM.write (0, memoAddr+1);
logData = 0;
if ((memoAddr+1) == 1023) memoAddr=1;
else memoAddr++;
}
ArduFarmBot GitHub
https://github.com/Mjrovai/ArduFarmBot
Schémas
ardufarmbot_qpNcBDX6Jr.fzz
Processus de fabrication
- Tendances émergentes dans l'implémentation IoT : Insights et stratégies
- Station de Mixage Automatique 6-Shooter Pilotée par Arduino – Construisez Votre Propre Robot Barman
- Système de Capteur de Température Multi-Position pour Intégration Domotique
- MotionSense : Détection d'intrusion intelligente avec Arduino et ESP8266
- Prévention incendie imprimante 3D : Système de sécurité à capteur intelligent
- Créer une jauge IoT connectée au Web avec Arduino, Yaler et IFTTT
- Lévitation magnétique répulsive avec Arduino : circuit simple et guide DIY
- Robot préhenseur télécommandé avancé pour manipulation de précision
- CoroFence : Détecteur Thermique Avancé PIR + Capteur Grid-EYE & IoT