Guides

ESP32 LoRa : Reseau Multi-Capteurs Longue Portee pour l’Agriculture Tunisienne

Architecture complete d'un reseau ESP32 + LoRa pour l'agriculture intelligente en Tunisie : nodes capteurs DHT22 deep sleep, gateway WiFi forwarding MQTT, CSMA-CA, encryption AES-128, autonomie solaire, et evolution LoRaWAN avec RAK7268.

5 avril 202612 min de lectureÉquipe Didactico

Octobre 2024, region de Kerkennah. Un olelculteur me decrit son cauchemar : 800 oliviers irrigues au goutte-a-goutte, repartis sur 15 hectares, avec 4 forages distincts. Chaque semaine, son employe parcourt 12 km a pied pour relever manuellement les compteurs d’eau et verifier les vannes. Cout salarie : 280 TND/mois. Erreurs frequentes. Pertes d’eau detectees parfois 5 jours apres. La solution moderne ? Un reseau de 18 nodes ESP32+LoRa, une gateway TTGO, un dashboard Grafana, et une consommation de 12 W au total pour le tout.

L’agriculture intelligente n’est plus de la science-fiction. C’est l’industrie qui transforme silencieusement les oliveraies de Sfax, les palmeraies de Tozeur, et les exploitations laitieres de Mahres. Et au coeur de cette revolution, deux composants : l’ESP32 (cerveau + WiFi) et le module LoRa (nerfs longue distance).

Ce guide architecture un vrai reseau multi-nodes pas un demo “hello world”. On parle CSMA-CA pour eviter les collisions, deep sleep + reveil precis sur timer, encryption AES-128, dashboard Grafana/InfluxDB, et evolution vers LoRaWAN avec gateway RAK7268.

1. Architecture maitre-esclave : la topologie qui marche

Avant de coder, il faut comprendre la topologie. Un reseau ESP32-LoRa typique a 3 elements :

  1. Nodes capteurs (N) : ESP32 + module LoRa + capteurs (DHT22, BME280, capacitif sol, etc.). En deep sleep 95% du temps. Reveil periodique, mesure, envoi LoRa, redormir.
  2. Gateway : ESP32 + LoRa + WiFi (ou 4G). Ecoute en continu les trames LoRa, les valide, les transforme et les pousse via MQTT vers le cloud.
  3. Cloud / Dashboard : broker MQTT (Mosquitto), base time-series (InfluxDB), visualisation (Grafana), alertes (Telegram bot).
  [Node 1]  [Node 2]   ...   [Node N]
    |        |                 |
    +--LoRa--+--LoRa--+--LoRa--+
              |
           [Gateway TTGO LoRa32]
              |
            WiFi/4G
              |
        [MQTT Broker]
              |
        [InfluxDB] --> [Grafana Dashboard]
              |
         [Telegram Bot alerts]

2. Choix hardware : TTGO LoRa32 vs Heltec V3 vs T-Beam

Modele MCU LoRa OLED GPS Batterie Prix Usage
TTGO LoRa32 V2.1 ESP32 SX1276 0,96″ NON JST 2 pin ~65 TND Gateway, node fixe
Heltec WiFi LoRa V3 ESP32-S3 SX1262 0,96″ NON JST 2 pin ~85 TND Gateway moderne
TTGO T-Beam v1.2 ESP32 SX1276 0,96″ NEO-6M 18650 holder ~110 TND Tracker, mobile
RFM95 nu + ESP32 WROOM ESP32 SX1276 NON NON Externe ~30 TND Node optimise sleep
💡 Recette gagnante

Notre setup recommande pour 90% des projets agricoles tunisiens : nodes = ESP32 WROOM + RFM95 nu (deep sleep < 20 µA), gateway = TTGO LoRa32 V2.1 (OLED pratique pour debug, ESP32 WiFi pour forwarder). Cout total 18 nodes + 1 gateway : ~600 TND.

3. Code Node : DHT22 + LoRa send + deep sleep

Le node doit etre obsedant sur l’energie. Chaque microampere compte. Cycle type : reveil > init capteurs (200 ms) > mesure (250 ms pour DHT22) > init LoRa (50 ms) > envoi (350 ms en SF10) > deep sleep (5 min). Temps eveil : ~1 sec. Temps dormance : 299 sec. Taux : 0,33%.

#include <SPI.h>
#include <LoRa.h>
#include <DHT.h>
#include <esp_sleep.h>

#define LORA_SS    18
#define LORA_RST   14
#define LORA_DIO0  26
#define DHT_PIN    4
#define DHT_TYPE   DHT22

#define NODE_ID    1  // Unique par node : 1, 2, 3...
#define SLEEP_SEC  300  // 5 minutes

DHT dht(DHT_PIN, DHT_TYPE);

// Compteur boot persistant en RTC memory
RTC_DATA_ATTR int bootCount = 0;

void setup() {
  Serial.begin(115200);
  bootCount++;
  Serial.printf("Reveil #%d node %dn", bootCount, NODE_ID);
  
  // Mesure capteur
  dht.begin();
  delay(2000);  // DHT22 needs 2 sec
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  float vbat = analogRead(35) * 3.3 / 4095 * 2;  // diviseur 1:1
  
  // Init LoRa
  LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0);
  if (!LoRa.begin(868E6)) {
    Serial.println("Echec LoRa");
    goSleep();
  }
  LoRa.setSpreadingFactor(10);
  LoRa.setSignalBandwidth(125E3);
  LoRa.setCodingRate4(5);
  LoRa.setTxPower(14);
  LoRa.enableCrc();
  
  // Trame : ID;BOOT;TEMP;HUM;VBAT
  String payload = String(NODE_ID) + ";" + bootCount + ";"
                 + String(t,1) + ";" + String(h,1) + ";" 
                 + String(vbat,2);
  
  // CSMA-CA simple : ecouter 500ms
  delay(random(50, 500));  // Backoff aleatoire pour eviter collisions
  
  LoRa.beginPacket();
  LoRa.print(payload);
  LoRa.endPacket();
  Serial.println("Envoye : " + payload);
  
  // Sleep
  LoRa.sleep();
  goSleep();
}

void goSleep() {
  Serial.println("Sleep " + String(SLEEP_SEC) + " sec");
  esp_sleep_enable_timer_wakeup(SLEEP_SEC * 1000000ULL);
  esp_deep_sleep_start();
}

void loop() {
  // Vide, setup() est rejoue apres chaque reveil
}

4. Code Gateway : LoRa receive + WiFi MQTT forward

#include <SPI.h>
#include <LoRa.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

#define LORA_SS    18
#define LORA_RST   14
#define LORA_DIO0  26

const char* ssid = "DIDACTICO_WIFI";
const char* pwd = "motdepasse";
const char* mqtt_server = "192.168.1.100";

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  Serial.begin(115200);
  
  WiFi.begin(ssid, pwd);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  Serial.println("WiFi OK " + WiFi.localIP().toString());
  
  client.setServer(mqtt_server, 1883);
  
  LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0);
  LoRa.begin(868E6);
  LoRa.setSpreadingFactor(10);
  LoRa.setSignalBandwidth(125E3);
  LoRa.enableCrc();
  
  Serial.println("Gateway pret");
}

void loop() {
  // Reconnect MQTT si besoin
  if (!client.connected()) {
    while (!client.connect("didactico-gateway")) delay(2000);
  }
  client.loop();
  
  // Ecouter LoRa
  int size = LoRa.parsePacket();
  if (size) {
    String msg = "";
    while (LoRa.available()) msg += (char)LoRa.read();
    int rssi = LoRa.packetRssi();
    float snr = LoRa.packetSnr();
    
    Serial.printf("RX [%d dBm / %.1f dB]: %sn", rssi, snr, msg.c_str());
    
    // Parser ID;BOOT;TEMP;HUM;VBAT
    int v[5]; int s = 0;
    String parts[5];
    for (int i = 0; i < msg.length(); i++) {
      if (msg[i] == ';') s++;
      else if (s < 5) parts[s] += msg[i];
    }
    
    // Construire JSON
    StaticJsonDocument<256> doc;
    doc["id"] = parts[0].toInt();
    doc["boot"] = parts[1].toInt();
    doc["temp"] = parts[2].toFloat();
    doc["hum"] = parts[3].toFloat();
    doc["vbat"] = parts[4].toFloat();
    doc["rssi"] = rssi;
    doc["snr"] = snr;
    
    String json;
    serializeJson(doc, json);
    
    String topic = "didactico/farm/node/" + parts[0];
    client.publish(topic.c_str(), json.c_str());
    Serial.println("Pub MQTT " + topic);
  }
}

5. Protocole : ASCII simple vs CBOR optimise

Le format de trame impacte fortement le temps en air (= consommation) et le duty cycle. Trois options :

ASCII delimite (ce qu’on a fait)

Simple a debugger, lisible, mais verbeux. 1;42;23.5;65.2;3.85 = 22 octets.

JSON

{"id":1,"t":23.5,"h":65.2} = 27 octets. A eviter en LoRa, gaspille du payload.

CBOR (RFC 8949)

Concise Binary Object Representation. Compact, parsable, type-safe. Meme donnee : 11 octets seulement (50% de gain). Utiliser la lib arduino-cbor de Joe Hewitt.

Binaire pack manuel

Pour le minimum absolu : packez en struct binaire de 8 octets.

// Trame compacte 8 octets
struct LoRaPacket {
  uint8_t  node_id;     // 0-255
  uint8_t  boot_low;
  uint16_t boot_high;
  int16_t  temp_x10;    // temp * 10, ex : 23.5 -> 235
  uint8_t  hum;         // 0-100%
  uint8_t  vbat_x20;    // vbat * 20, ex : 3.85V -> 77
} __attribute__((packed));

LoRaPacket pkt;
pkt.node_id = 1;
pkt.temp_x10 = (int16_t)(temp * 10);
// ...
LoRa.beginPacket();
LoRa.write((uint8_t*)&pkt, sizeof(pkt));
LoRa.endPacket();

6. Gerer plusieurs nodes : CSMA-CA et collisions

Avec 1 node, aucun probleme. Avec 20 nodes qui se reveillent tous a la meme seconde, vous avez collision garantie : les paquets se superposent, le gateway ne recupere rien.

Solution 1 : decalage temporel (TDMA simplifie)

Le node N attend N*10 secondes avant d’envoyer. Node 1 -> T+10s, Node 2 -> T+20s, etc. Simple mais rigide.

Solution 2 : CSMA-CA (Carrier Sense Multiple Access)

Avant d’emettre, le node ecoute si le canal est libre. S’il y a du trafic, il attend un temps aleatoire (backoff) puis recommence.

bool isChannelBusy() {
  // Lire RSSI ambiant
  int rssi = LoRa.rssi();
  return (rssi > -90);  // si signal > -90 dBm = occupe
}

void sendWithCSMA(String msg) {
  int attempts = 0;
  while (isChannelBusy() && attempts < 5) {
    int backoff = random(100, 1000) * (1 << attempts);
    delay(backoff);
    attempts++;
  }
  LoRa.beginPacket();
  LoRa.print(msg);
  LoRa.endPacket();
}

Solution 3 : ACK + retry

Le gateway repond par un ACK apres reception. Le node retransmet si pas d’ACK apres 2 secondes. Maximum 3 tentatives.

7. Encryption AES-128 sur LoRa

Par defaut, LoRa transmet en clair. N’importe qui avec un module RFM95 peut sniffer vos donnees. Pour les usages serieux (agriculture commerciale, smart city), il faut chiffrer.

L’ESP32 a un module AES hardware. Utilisez la lib AESLib ou mbedtls.

#include <AESLib.h>

AESLib aes;
byte aes_key[16] = {0x44, 0x49, 0x44, 0x41, 0x43, 0x54, 0x49, 0x43,
                    0x4F, 0x32, 0x30, 0x32, 0x36, 0x4B, 0x45, 0x59};
byte aes_iv[16];

void sendEncrypted(String plain) {
  // Padder a multiple de 16
  byte plainBuf[64];
  memset(plainBuf, 0, 64);
  plain.getBytes(plainBuf, plain.length() + 1);
  
  byte cipherBuf[64];
  aes.set_IV((uint64_t)millis());
  aes.encrypt(plainBuf, 32, cipherBuf, aes_key, 128, aes_iv);
  
  LoRa.beginPacket();
  LoRa.write(aes_iv, 16);   // IV en clair (necessaire pour decryptage)
  LoRa.write(cipherBuf, 32);
  LoRa.endPacket();
}
⚠️ Cle AES = cle bancaire

La cle AES doit etre stockee dans le NVS ou dans la zone eFuse de l’ESP32 (irrecuperable). NE JAMAIS la committer dans un repository git public. Utilisez le secure boot ESP32 en production. Si la cle fuite, tous les nodes deployes sont compromis.

8. Energie : LiPo 2000 mAh + panneau solaire 6V 1W

Pour une autonomie illimitee, le combo gagnant est :

  • Batterie LiPo 2000 mAh (couts ~20 TND)
  • Panneau solaire 6V 1W (etanche, ~25 TND)
  • Module MPPT TP4056 + protection (charge intelligent, ~5 TND)
  • Coffret IP65 exterieur (~15 TND)

Bilan energetique : ESP32 + LoRa + DHT22 en deep sleep cycle 5 min

Sleep (299 sec) 20 µA = 5,98 mAs/cycle
Wake mesure + envoi (1 sec) 120 mA = 120 mAs/cycle
Total/cycle 125,98 mAs
Cycles/jour 288 (1 toutes 5 min)
Consommation/jour ~10 mAh
Autonomie batterie 2000 mAh seule ~200 jours
Avec panneau solaire 6V 1W (4h/jour) illimitee

9. Cas d’usage agriculture intelligente Tunisie

Oliveraies de Kerkennah (capteur humidite sol)

18 nodes (capacitif sol + DHT22 air) sur 15 ha. Gateway TTGO sur depot. Donnees vers serveur OVH (Roubaix). Dashboard Grafana avec carte par parcelle. Alertes Telegram si humidite < 25%. Economie eau estimee : 30%.

Palmeraies Tozeur (niveau cuve eau)

10 cuves de 5000L avec capteur ultrasonique JSN-SR04T. Gateway 4G (SIM Orange) car pas de WiFi. Envoi 4x/jour. Notification SMS au gerant si cuve < 20%.

Tracker betail Mahres (T-Beam GPS)

40 colliers T-Beam sur vaches laitieres. Position GPS envoyee 1x/heure. Geofencing au niveau gateway : alerte si une vache quitte le perimetre. Detection vol resolu en 2 heures (vs 3 jours avant).

Serres connectees Sahel

5 serres avec capteurs DHT22 + BH1750 (luminosite) + capteur CO2 (MH-Z19B) + sol. Reseau LoRa interne, gateway WiFi vers cloud. Controle automatique aerateurs et ombrieres.

10. Comparaison NB-IoT Orange Tunisie

Orange Tunisie a deploye un reseau NB-IoT (Narrowband IoT) sur ses tours 4G. Comparaison frontale :

Critere LoRa P2P/LoRaWAN NB-IoT Orange
Cout module 15 TND (RFM95) 40 TND (SIM7080)
Abonnement 0 TND (P2P) 10-30 TND/mois
Couverture 10-15 km LoRa Reseau cellulaire
Gateway requise OUI (custom) NON
Penetration interieur Moyenne Excellente
Latence 1-5 sec 10-30 sec
Autonomie batterie 2-5 ans 1-3 ans
💡 Quand choisir NB-IoT

Si vos nodes sont disperses sur des dizaines de kilometres (pas concentres sur 10 km), ou en interieur dense (sous-sol immeuble), NB-IoT est plus pertinent. Pour une exploitation agricole compacte avec un point haut pour la gateway, LoRa gagne sur le ROI a partir de 5 nodes.

11. Evolution vers LoRaWAN avec RAK7268

Quand vous depassez 20-30 nodes ou voulez exposer vos donnees a TTN (The Things Network), passez en LoRaWAN avec une vraie gateway 8 canaux.

La RAK7268 (RAKwireless) est la gateway industrielle reference : 8 canaux SX1302, GPS, Ethernet, WiFi, 4G optionnel, boitier IP30 indoor (RAK7268C pour outdoor IP67). Cout ~600 TND.

Configurer LoRaWAN cote node avec la lib MCCI LMIC. ADR (Adaptive Data Rate) ajuste automatiquement le SF et la TX power selon la qualite du lien. ChirpStack (open-source) ou TTN community fournit le network server.

12. Dashboard Grafana + InfluxDB

Architecture cloud type :

  1. MQTT broker Mosquitto sur VPS (~25 TND/mois OVH Eco)
  2. Telegraf ecoute MQTT, transforme en time-series
  3. InfluxDB 2.x stocke (retention 1 an, ~500 Mo pour 18 nodes)
  4. Grafana visualise (gratuit OSS ou Grafana Cloud free tier)
  5. Alerting Grafana > Telegram bot
# docker-compose.yml gateway cloud Didactico
version: '3'
services:
  mosquitto:
    image: eclipse-mosquitto:2
    ports: ["1883:1883", "9001:9001"]
    volumes: ["./mosquitto:/mosquitto/config"]
  influxdb:
    image: influxdb:2.7
    ports: ["8086:8086"]
    volumes: ["influx-data:/var/lib/influxdb2"]
  telegraf:
    image: telegraf:1.28
    depends_on: [mosquitto, influxdb]
    volumes: ["./telegraf.conf:/etc/telegraf/telegraf.conf:ro"]
  grafana:
    image: grafana/grafana:10
    ports: ["3000:3000"]
    volumes: ["grafana-data:/var/lib/grafana"]
volumes:
  influx-data:
  grafana-data:
🌾
RECOMMANDE AGRICULTURE

TTGO LoRa32 V2.1 – Gateway tout-en-un

ESP32 + SX1276 868 MHz + OLED + lecteur SD + batterie. La carte ideale pour votre gateway LoRa-to-WiFi. Compatible avec tous nos nodes RFM95 et T-Beam disponibles en stock Didactico Sfax.

Decouvrir TTGO →

13. FAQ – ESP32 LoRa Reseau Capteurs

Combien de nodes peut gerer une gateway ESP32 ?

En P2P custom avec CSMA-CA : 20-30 confortablement. En LoRaWAN avec gateway 8 canaux (RAK7268) : 1000+. Le facteur limitant est le duty cycle, pas le hardware ESP32.

Quel SF utiliser pour un reseau de 15 nodes sur 5 km ?

SF9 ou SF10 sur 868 MHz BW125. Trame courte (~200 ms), capacite ~30 messages/heure par node sans saturer. Pour SF12, vous saturez le canal des 8-10 messages/heure.

Le DHT22 est-il fiable en exterieur ?

Oui dans un boitier ventile abrite. NON exposé directement au soleil ou pluie. Pour les conditions extremes, prefere BME280 (plus precis, I2C) ou SHT31 (industriel).

Faut-il un MOSFET pour couper les capteurs en deep sleep ?

Pour des projets pousses oui. Un DHT22 consomme 1,5 mA permanent meme non interroge. Un MOSFET P-channel commute par GPIO ESP32 economise 100% de cette conso pendant le sleep, allongeant l’autonomie de 40%.

Que faire si la gateway plante ?

(1) Watchdog hardware ESP32 (deja active par defaut). (2) Reboot si pas de trame recue pendant 30 min. (3) Idealement, 2 gateways redondantes. (4) Monitoring du gateway via heartbeat MQTT toutes les minutes.

Peut-on utiliser Wi-Fi ET LoRa en meme temps sur ESP32 ?

Oui, c’est le principe meme du gateway TTGO LoRa32. ESP32 multitasking gere les deux radios. Attention juste a ne pas faire d’envoi LoRa pendant une connexion WiFi intensive (peut causer reset par pic de conso).

Combien coute un deploiement complet 15 nodes + gateway + cloud ?

Hardware : 15x (ESP32 + RFM95 + batterie + panneau + boitier) = 15x ~70 TND = 1050 TND. Gateway TTGO LoRa32 + boitier : 100 TND. VPS Cloud OVH Eco : 25 TND/mois. Total deploiement initial ~1150 TND.

Conclusion : l’agriculture intelligente, c’est maintenant et en Tunisie

L’ESP32 + LoRa n’est pas une futurologie. C’est une realite operationnelle aujourd’hui en Tunisie, deployee chez des dizaines de producteurs de Sfax, Tozeur, Mahres, Kerkennah et du Sahel. L’investissement initial (1000-1500 TND) est amorti en 6-12 mois par les economies d’eau, de carburant, et de main-d’oeuvre.

Pour vos projets PFE en ENIT, INSAT, ESPRIT ou ISET autour de l’IoT agricole, Didactico Sfax fournit tous les composants (TTGO LoRa32, T-Beam, RFM95, DHT22, BME280, capacitifs sol, batteries, panneaux solaires) avec livraison 24-48h en Tunisie. Decouvrez aussi notre guide Module LoRa SX1276 longue portee et ESP32 WiFi envoi donnees Internet.

🛒 Matériel nécessaire pour ce guide

Tout le matériel de ce tutoriel est disponible chez Didactico — livraison 24-48h partout en Tunisie, paiement à la livraison.

Voir toute la catégorie « Cartes Programmables » →

📘 Guides liés : ESP32 WiFi : Envoyer vos Donnees IoT sur Internet (Guide Complet) · Module LoRa Arduino SX1276 : Communication 10 km Longue Portée

🛒 Passez à la pratique
Retrouvez nos modules LoRa en stock chez Didactico, livraison partout en Tunisie.
Voir la boutique →