Le problème : capter une pression sur une machine de 20 ans, sans toucher l’automate
Tu as une presse à injection de 1998, ou un métier à tisser ratière de 2003, ou une cuve fermentation inox de 2005. L’équipement tourne bien, la mécanique est saine, et personne ne veut y toucher. Mais la direction te demande la pression hydraulique en temps réel, ou la T° cuve à la minute, ou la conso d’air comprimé — bref, des données. L’automate d’origine est un LOGO! 8 qui ne parle rien d’autre qu’un vieux Modbus RTU propriétaire, ou carrément un TSX Nano qui ne parle rien du tout. Tirer un câble neuf jusqu’à la GTC coûterait 800 € le point, et l’intégrateur historique facture 2 500 € HT le “module IIoT compatible”.
Il y a un chemin qui tient dans la main et coûte moins de 90 € par point : un ESP32 qui lit le transmetteur 4-20mA existant (ou ajouté), publie en Modbus TCP sur le réseau IT, et Node-RED récupère le tout pour le pousser dans InfluxDB → Grafana. Pas de passerelle commerciale, pas de modification de l’automate, pas d’arrêt machine pour le déploiement.
Le 4-20mA est la norme industrielle depuis les années 70. 90 % des transmetteurs de process (pression, T°, débit, niveau) existants en 2026 parlent encore cette boucle de courant. Un ESP32 + un module d’entrée 4-20mA isolé suffit à en faire une source de données IIoT.
Ce tuto montre la chaîne de bout en bout, testée sur banc BCUB3. BOM, câblage, firmware, flow Node-RED, dashboard Grafana, et les pièges mesurés — pas de “il suffit de”.
1. Pourquoi le 4-20mA résiste encore en 2026
La boucle 4-20mA (IEC 60381-1) domine l’instrumentation de process pour trois raisons mécaniques qui n’ont rien à voir avec la mode :
- Intensité > tension pour la transmission longue distance. Une boucle de courant est insensible à la résistance du câble (jusqu’à plusieurs centaines d’ohms) alors qu’un signal en tension s’effondre sur 50 m de paire torsadée.
- Détection de rupture native : 0 mA ≠ 4 mA = “vivant à 0 %”. Si la boucle casse, tu lis 0 mA et tu sais que c’est un défaut, pas une mesure à 0.
- Alimentation via la boucle : en 2-wire, le capteur est alimenté par la même paire qui transporte la mesure. Simplifie l’installation.
Concurrents modernes (IO-Link, HART, Profinet) gagnent en fonctionnalités mais perdent en compatibilité legacy. En rétrofit, tu gardes le 4-20mA.
Équation de base : le transmetteur convertit une grandeur physique X ∈ [Xmin, Xmax] en courant I ∈ [4 mA, 20 mA] :
I = 4 + 16 × (X - Xmin) / (Xmax - Xmin) [mA]
Exemple transmetteur de pression Huba 511 calibré 0-10 bar → 0 bar = 4 mA, 10 bar = 20 bar = 20 mA, 5 bar = 12 mA. Un ADC sur résistance shunt retransforme le courant en tension mesurable par un µC.
2. Architecture système
L’architecture est lisible de gauche à droite :
[Capteur 4-20mA Huba/WIKA/IFM]
│ boucle courant 2-wire
▼
[Alim 24V dédiée 0,5 A]
│
▼
[Module d'entrée 4-20mA isolé ─ 165 Ω shunt + ADC]
│ SPI / I2C / analog
▼
[ESP32-S3 DevKitC-1] ← WiFi VLAN OT
│ Modbus TCP
▼
[Réseau IT — switch managé]
│
▼
[Serveur Linux ─ Node-RED + Mosquitto + InfluxDB + Grafana]
│
▼
[Dashboard opérateur + alertes Slack / mail / WhatsApp]
Rôles :
- ESP32-S3 : serveur Modbus TCP (rôle esclave). Il expose 2 registres holding : la valeur brute ADC (16b unsigned) et la valeur ingénierie (Real 32b scaled).
- Node-RED : client Modbus TCP (rôle maître), polling 1 s. Transforme + publie sur MQTT ou écrit direct dans InfluxDB.
- InfluxDB (v2 ou v3) : TSDB qui stocke les time series.
- Grafana : dashboard live + alertes.
- Mosquitto (optionnel) : broker MQTT si tu as plusieurs Node-RED ou si tu veux un pattern pub/sub.
Le choix Modbus TCP côté ESP32 est volontaire : c’est le plus petit dénominateur commun avec les SCADA existants (WinCC, Indusoft, Ignition, Citect) et avec Node-RED. Si tu démarres de zéro et que tu contrôles toute la stack, MQTT direct est plus léger — on en parle en section 9.
Point réseau crucial : isole l’ESP32 sur un VLAN OT ou un SSID WiFi dédié. Pas d’exposition directe Internet. Un capteur de rétrofit qui signale une fuite de pression ne doit jamais transiter par le WAN public sans tunnel.
3. Bill of Materials (BOM)
Prix constatés en avril 2026, Amazon FR / Kubii / RS Components FR, TTC particulier. Total ~90 € HT hors automate, hors serveur (celui-ci peut être un Raspberry Pi 4 déjà en place).
| Composant | Référence | Prix TTC | Fournisseur |
|---|---|---|---|
| ESP32-S3 DevKitC-1 N16R8 | Espressif, 8 Mo PSRAM | 24 € | Amazon FR |
| Module entrée 4-20mA isolé | Mikroelektronika “4-20mA R Click” (MCP3201 12b) | 18 € | Mouser |
| Résistance précision 165 Ω 0,1% | Vishay MRS25 | 3 € | RS Components |
| Alim industrielle 24V 0,5A rail DIN | MeanWell HDR-15-24 | 22 € | RS Components |
| Transmetteur pression 4-20mA 0-10 bar (démo) | Huba 511.934 ou équivalent | 32 € | RS Components |
| Bornier DIN rail + boîtier IP54 | Italtronic 22.35 + Wago 221 | 15 € | Amazon FR |
| Câble paire torsadée blindée | Lapp Unitronic 2×0,5 mm² 10 m | 9 € | Amazon FR |
| Fusible rail DIN + porte-fusible 0,5 A | Schurter FUT-32 | 7 € | RS Components |
Total dev + 1 point prod : 130 € TTC (en comptant le capteur Huba). Sans le capteur (si le transmetteur existe déjà sur la machine), on descend à ~90 € TTC.
4. Câblage 4-20mA 2-wire
Le câblage est le point le plus sensible du rétrofit. Une erreur ici et tu lis du bruit, ou tu déclenches les protections de l’alim.
Alim 24V (+) ────┬──────────────► Transmetteur Huba 511 (+)
│
[Fusible 0,5A]
│
Alim 24V (-) ◄───┴──────┐
│
▼
Transmetteur (−) ──────► Module 4-20mA Click IN(+)
│
[Résistance shunt 165 Ω 0.1%]
│
Module 4-20mA Click IN(-)
│
GND commun ───► ESP32 GND
Points critiques :
- Isolation galvanique : le module Mikroelektronika “4-20mA R Click” intègre un optocoupleur — la masse 24V de l’alim industrielle n’est pas reliée directement à la masse 3,3V de l’ESP32. En rétrofit, c’est obligatoire sous peine de boucles de masse qui tuent le capteur au premier orage.
- Résistance shunt 165 Ω : choix calculé pour lire 20 mA × 165 Ω = 3,3 V max, qui est exactement la pleine échelle de l’ADC ESP32-S3 en mode 0 dB. Pour un ADC externe 12b (MCP3201 sur le click), on reste à 3,3 V ref.
- Fusible 0,5 A : protection boucle. Un capteur 4-20mA en défaut tire jamais plus de 25 mA ; un fusible 0,5 A protège en cas de court-circuit aval.
- Masse unique : le GND du module doit rejoindre le GND ESP32, mais pas la masse 24V. La séparation est la valeur ajoutée du module isolé.
Une erreur classique en rétrofit : alimenter le transmetteur depuis l’alim 5V USB de l’ESP32. Ça semble marcher 10 minutes, puis le régulateur USB tombe en protection thermique quand la boucle atteint 20 mA. Toujours une alim 24V dédiée sur rail DIN.
5. Firmware ESP32 — serveur Modbus TCP
On utilise la lib eModbus (Arduino/ESP-IDF), la plus mature en 2026 pour ESP32.
5.1 platformio.ini
[env:esp32-s3-devkitc-1]
platform = espressif32@6.5.0
board = esp32-s3-devkitc-1
framework = arduino
monitor_speed = 115200
build_flags =
-DCORE_DEBUG_LEVEL=3
-DBOARD_HAS_PSRAM
lib_deps =
eModbus/eModbus@^1.7.2
adafruit/Adafruit MCP3XXX Library@^1.0.0
5.2 Firmware principal
// main.cpp — ESP32-S3 serveur Modbus TCP exposant une mesure 4-20mA
// Register map :
// HR[0] : valeur brute ADC (0..4095)
// HR[1] : valeur ingénierie en 1/100 de bar (0..1000 pour 0..10 bar)
// HR[2] : heartbeat counter (incrémenté chaque seconde)
#include <Arduino.h>
#include <WiFi.h>
#include <SPI.h>
#include <Adafruit_MCP3008.h>
#include <ModbusServerTCPasync.h>
#define SSID "VOTRE_SSID_OT"
#define PASSWORD "VOTRE_MDP"
#define MB_PORT 502
#define MB_ID 1
// Calibration — à ajuster avec un calibrateur de boucle avant prod
#define ADC_AT_4MA 819 // 4 mA × 165Ω = 0,66 V → 12b ADC = 819
#define ADC_AT_20MA 4095 // 20 mA × 165Ω = 3,3 V = pleine échelle
#define PRESSURE_MIN_BAR 0
#define PRESSURE_MAX_BAR 10
ModbusServerTCPasync MBserver;
Adafruit_MCP3008 adc;
uint16_t hregs[3] = {0, 0, 0};
volatile uint32_t lastSample = 0;
// Worker Modbus — lit HR[0..2]
ModbusMessage readHR(ModbusMessage req) {
uint16_t addr, words;
req.get(2, addr);
req.get(4, words);
if (addr + words > 3) {
return ModbusMessage(req.getServerID(), req.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
}
ModbusMessage resp;
resp.add(req.getServerID(), req.getFunctionCode(), (uint8_t)(words * 2));
for (uint16_t i = 0; i < words; i++) resp.add(hregs[addr + i]);
return resp;
}
void sampleLoop() {
if (millis() - lastSample < 1000) return;
lastSample = millis();
// Moyenne glissante 16 échantillons pour filtrer 50 Hz
uint32_t sum = 0;
for (int i = 0; i < 16; i++) sum += adc.readADC(0);
uint16_t raw = sum / 16;
// Conversion linéaire vers bar × 100 pour garder 2 décimales sur 16 bits
long pressure_cbar = 0;
if (raw > ADC_AT_4MA) {
pressure_cbar = (long)(raw - ADC_AT_4MA) * (PRESSURE_MAX_BAR - PRESSURE_MIN_BAR) * 100
/ (ADC_AT_20MA - ADC_AT_4MA);
}
if (pressure_cbar < 0) pressure_cbar = 0;
if (pressure_cbar > 65535) pressure_cbar = 65535;
hregs[0] = raw;
hregs[1] = (uint16_t)pressure_cbar;
hregs[2]++;
Serial.printf("[sample] raw=%u P=%.2f bar heartbeat=%u\n",
raw, pressure_cbar / 100.0, hregs[2]);
}
void setup() {
Serial.begin(115200);
delay(500);
SPI.begin();
adc.begin();
WiFi.mode(WIFI_STA);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.printf("\n[wifi] OK ip=%s\n", WiFi.localIP().toString().c_str());
MBserver.registerWorker(MB_ID, READ_HOLD_REGISTER, &readHR);
MBserver.start(MB_PORT, 4, 10000);
Serial.printf("[mb] server up :%d id=%d\n", MB_PORT, MB_ID);
}
void loop() {
sampleLoop();
delay(10);
}
Points d’attention :
- Moyenne 16 échantillons : filtre matériel passe-bas numérique. Supprime le 50 Hz industriel qui se couple inévitablement sur la boucle. Sur un process à faible dynamique (pression process, T°), c’est suffisant. Pour un signal rapide, passer à un filtre IIR ou un RC côté shunt.
- Calibration :
ADC_AT_4MAetADC_AT_20MAsont à ajuster avec un calibrateur de boucle (type Fluke 789 ou équivalent). Les valeurs en dur ci-dessus sont théoriques — en pratique, chaque ADC a un offset de 20-50 LSB qu’il faut mesurer. - Heartbeat : HR[2] s’incrémente chaque seconde. Si Node-RED voit ce compteur figé, il sait que l’ESP32 a crashé même si la connexion TCP tient.
6. Débogage terminal + Modbus poll
Avant de brancher Node-RED, valide la chaîne capteur → ESP32 → Modbus TCP avec mbpoll depuis un Linux. C’est l’outil CLI de référence en 2026.
# Install (Debian / Ubuntu / Raspberry Pi OS)
sudo apt install mbpoll
# Lire les 3 registres holding (HR[0..2])
mbpoll -m tcp -a 1 -t 4 -r 1 -c 3 -1 192.168.10.42
# -m tcp : transport Modbus TCP
# -a 1 : slave id
# -t 4 : holding registers
# -r 1 : adresse de départ (mbpoll compte à partir de 1)
# -c 3 : 3 registres
# -1 : polling unique (enlever pour polling continu)
Sortie attendue :
-- Polling slave (Ctrl-C to stop) --
[1]: 2458 # ADC raw (≈ 12 mA soit ≈ 5 bar)
[2]: 500 # 5,00 bar en 1/100
[3]: 73 # heartbeat
Si tu obtiens Connection refused : firewall/VLAN. Si Illegal Data Address : mismatch map registres. Si la valeur [1] ne bouge pas quand tu presses le capteur : câblage boucle à reprendre.
7. Node-RED flow → InfluxDB
Import ce flow dans Node-RED (Menu → Import → Paste JSON) :
[
{"id":"inj","type":"inject","name":"1 s tick","repeat":"1","topic":"poll","x":120,"y":120,"wires":[["read"]]},
{"id":"read","type":"modbus-read","name":"ESP32 HR[0..2]","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":true,"unitid":"1","dataType":"HoldingRegister","adr":"0","quantity":"3","rate":"","rateUnit":"ms","delayOnStart":false,"startDelayTime":"","server":"mb-srv","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":320,"y":120,"wires":[["scale"],[]]},
{"id":"mb-srv","type":"modbus-client","name":"ESP32","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"192.168.10.42","tcpPort":"502","tcpType":"DEFAULT","unit_id":"1","commandDelay":"1","clientTimeout":"1000","reconnectOnTimeout":true,"reconnectTimeout":"2000","parallelUnitIdsAllowed":true,"showErrors":false,"showWarnings":false,"showLogs":false},
{"id":"scale","type":"function","name":"scale → bar","func":"const [raw, cbar, hb] = msg.payload;\nmsg.payload = { raw, pressure_bar: cbar/100, heartbeat: hb };\nreturn msg;","outputs":1,"timeout":0,"x":520,"y":120,"wires":[["influx"]]},
{"id":"influx","type":"influxdb out","name":"bcub3_retrofit","influxdb":"influx-cfg","measurement":"pressure_huba_511","precision":"s","retentionPolicy":"","database":"","precisionV18FluxV20":"s","retentionPolicyV18Flux":"","organisation":"bcub3","bucket":"retrofit","x":720,"y":120,"wires":[]},
{"id":"influx-cfg","type":"influxdb","hostname":"127.0.0.1","port":"8086","protocol":"http","database":"","name":"InfluxDB 2.x","usetls":false,"tls":"","influxdbVersion":"2.0","url":"http://127.0.0.1:8086","timeout":10,"rejectUnauthorized":false}
]
Ce que le flow fait :
- Tick 1 s → déclenche un read Modbus TCP sur HR[0..2].
- Fonction
scaleconvertitcbaren bar et restructure le payload. - Écriture InfluxDB dans le bucket
retrofit, measurementpressure_huba_511, tags{machine}à ajouter si plusieurs points.
Alternative MQTT : si tu as déjà un broker Mosquitto, remplace le node InfluxDB par un node MQTT-out publishing sur bcub3/retrofit/press/cuve_1. Node-RED côté serveur abonné au topic relaie ensuite vers InfluxDB. Pattern pub/sub scale mieux au-delà de 5 ESP32.
8. Dashboard Grafana
Source Grafana v10+, datasource InfluxDB 2.x Flux.
Panel 1 — Courbe pression live
Query Flux :
from(bucket: "retrofit")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r._measurement == "pressure_huba_511")
|> filter(fn: (r) => r._field == "pressure_bar")
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|> yield(name: "mean")
Time series panel, unit pressurebar, seuil warning > 8 bar (rouge), alerte critical > 9,5 bar (annotation mail/Slack).
Panel 2 — Heartbeat liveness
Single-stat panel, query identique avec r._field == "heartbeat". Alerte : si delta(last_1m) < 30 → heartbeat figé → incident technique (ESP32 crashé ou réseau HS).
Alerte Grafana → notification
Rule Grafana :
IF pressure_bar > 9.5 FOR 10s→ severitycritical→ contact point Slack / WhatsApp BCUB3.IF heartbeat_delta_1m == 0→ severitywarning→ contact point mail maintenance.
9. Sécurité industrielle — les règles non négociables
Un capteur rétrofit qui remonte une pression n’est jamais “juste un capteur”. C’est une nouvelle surface d’attaque réseau + un équipement à la frontière IT/OT. Les règles suivantes sont minimales et non négociables en 2026.
- Isolation galvanique entre la boucle 4-20mA (référence masse 24V industrielle) et l’ESP32 (référence masse 3,3V électronique). Un simple opto ou un module click isolé coûte 5 € et évite la destruction du capteur au premier coup d’orage ou déclenchement de disjoncteur différentiel.
- Alimentation séparée du transmetteur (24V DC 0,5 A rail DIN) et de l’ESP32 (5V USB ou PoE). Ne partage jamais l’alim USB de l’ESP32 pour boucler le 4-20mA.
- Fusible 0,5 A sur le + 24V en amont de la boucle. Protection incendie en cas de court-circuit sur une longueur de câble importante.
- Mise à la terre : le blindage du câble paire torsadée est mis à la terre à une seule extrémité (côté alim), jamais aux deux (boucle de masse).
- VLAN OT dédié pour l’ESP32. Pas de route directe vers Internet, pas de route vers la bureautique. Firewall restreignant au strict nécessaire (seul le serveur Node-RED peut joindre le port 502 de l’ESP32).
- Firmware signé et OTA activé avec vérification de signature. En 2026, un ESP32 rétrofit sans OTA signé est un équipement qu’on ne peut pas patcher quand une CVE ESP-IDF tombe. Inacceptable en industrie.
- Sauvegarde automate d’origine avant tout essai, même si tu ne touches pas au câblage automate (on touche toujours au câblage automate sans faire exprès un jour sur deux).
Retenir : le rétrofit IIoT est une intervention industrielle, pas un hackathon maker. Le capteur remonte une donnée qui sert à décider — si elle est fausse, quelqu’un paye.
10. Pitfalls mesurés en production
Problèmes rencontrés sur banc BCUB3 et chez des intégrateurs partenaires, avec la parade.
| # | Symptôme | Cause racine | Parade |
|---|---|---|---|
| 1 | Valeur oscille de ±0,3 bar toutes les secondes | Couplage 50 Hz industriel sur la boucle | Moyenne 16+ échantillons + câble blindé + shunt ≤ 250 Ω |
| 2 | Valeur gelée après 3-4 h | Stack WiFi ESP32 figée en mode AP scan | Watchdog matériel + reboot si heartbeat Modbus silencieux 30 s |
| 3 | Latence 3-5 s sur lecture Modbus | Node-RED et ESP32 sur SSID différents — scan WiFi | Isoler ESP32 sur un AP unique dédié OT |
| 4 | ”Connection refused” au bout d’1 h | Limite de sessions TCP ESP32 dépassée — client Modbus Node-RED qui ne ferme pas | reconnectOnTimeout: true côté Node-RED + serverTimeout: 10000 côté ESP32 |
| 5 | Drift linéaire +0,5 bar / 24 h | Dérive thermique ADC ESP32 (chaleur enceinte) | Calibration 2 points (zéro + plein) hebdo en auto, ou passage à ADC externe (MCP3201) |
| 6 | Reboot inopiné à 22h pile | Mise à jour OTA concurrente avec read Modbus | Fenêtre OTA hors heures prod, lock mutex sur l’ADC |
Ces pièges sont documentés dans le REX rétrofit BCUB3 (à paraître) avec les fichiers logs bruts.
11. Quand le rétrofit dépasse le DIY
Le pattern ESP32 + 4-20mA + Node-RED + Grafana couvre largement 70 % des besoins de rétrofit d’une ligne industrielle. À partir de 50+ points mesurés, ou quand la criticité impose un SIL ≥ 2 (sécurité fonctionnelle IEC 61508), ou quand tu dois remonter dans un MES ou ERP, le rétrofit DIY atteint ses limites.
Les questions qui apparaissent alors : gestion de flotte (OTA de 200 ESP32), durcissement sécurité (PKI, 802.1X, segmentation OT/IT/DMZ), redondance brokers MQTT, archivage conforme, traçabilité par lot, intégration à un historian type Wonderware / Aveva PI. C’est le moment où un accompagnement structuré — cartographie SI, revue d’architecture, plan de déploiement — devient plus rentable que de continuer en DIY.
CTA — Trois niveaux
💰 Le kit complet de ce tuto, prêt à commander
Les composants exacts utilisés sur le banc BCUB3, liens affiliés Amazon FR / RS Components FR / Mouser. Commander depuis ces liens soutient BCUB3 sans surcoût.
- ESP32-S3 DevKitC-1 N16R8 — 24 € Amazon FR
- Module 4-20mA Click isolé — 18 € Mouser
- Alim 24V 0,5A rail DIN MeanWell HDR-15-24 — 22 € RS Components
- Transmetteur pression Huba 511 0-10 bar — 32 € RS Components
💬 Besoin d’aide pour déployer ça sur votre parc machines ?
Ce tuto couvre le cas “1 capteur, 1 ESP32, 1 dashboard dev”. En production, multiplier par 30, 50, 200 points fait apparaître des sujets : supervision de la flotte, OTA signée, segmentation réseau, redondance, intégration MES.
BCUB3 propose un cadrage gratuit de 45 minutes pour évaluer ton contexte rétrofit, clarifier l’architecture cible, et chiffrer un accompagnement adapté (revue de code firmware, conseil archi OT, hotline déploiement).
→ Prendre RDV de cadrage (45 min, gratuit)
🎓 Mission d’accompagnement BCUB3 — cadrage et mise en œuvre
Si le rétrofit IIoT s’inscrit dans un projet plus large — architecture OT/IT, feuille de route IIoT, priorisation de use cases — BCUB3 propose deux formats d’accompagnement :
- Cadrage 1 jour — 499 € HT : audit de ton contexte rétrofit, options d’architecture, chiffrage indicatif du déploiement.
- Mission 3 jours — 1 499 € HT : cadrage + prototype ciblé (1-3 capteurs instrumentés, dashboard Grafana, documentation de reprise). Format intra-entreprise.
→ Discuter d’un accompagnement →
Pour aller plus loin
- ESP32 + OPC UA + Siemens S7-1200 : tuto end-to-end — le même pattern mais en OPC UA natif Siemens, pour un automate neuf.
- Architecture réseau industriel Purdue — où placer ESP32, switch, SCADA dans une archi OT propre.
- Agrégation de données industrielles — architecturer la couche TSDB + Grafana derrière un parc de capteurs.
- XGBoost pour la maintenance prédictive — exploiter les données remontées par rétrofit pour prédire les défaillances.