← Blog

ESP32 + OPC UA + Siemens S7-1200 : tuto end-to-end

Remonter un capteur ESP32 vers un automate Siemens S7-1200 en OPC UA, sans passerelle commerciale à 1500€. Architecture, code, wiring, benchmark coût/perf.

Le problème : remonter une T° four vers un S7-1200 sans passerelle à 1500€

Tu as un four d’étuvage composite. L’automate de ligne est un Siemens S7-1200 (le cheval de trait des ETI françaises, on en croise dans 6 usines sur 10). Le client veut enregistrer la température de deux nouveaux points — l’un au-dessus de la vitre, l’autre dans la sole basse. Le câblage 4-20mA existant est saturé, tirer deux paires torsadées blindées sur 35 m de chemin de câbles coûte 1 400 € au mètre linéaire installé. Le devis intégrateur pour ajouter un module d’entrées analogiques décentralisé type Weidmüller UR20 ou Phoenix Contact AXL F coûte entre 1 500 € et 2 200 € HT, hors mise en service.

Il existe une alternative qui tient dans la paume de la main et coûte moins de 90 € : un ESP32-S3 qui lit les capteurs localement, et qui remonte les valeurs au S7-1200 en OPC UA, sur le réseau Ethernet de production déjà en place. Pas de passerelle commerciale, pas de câblage supplémentaire, et un chemin de migration propre vers Profinet si le besoin grossit un jour.

Le S7-1200 intègre un serveur OPC UA depuis le firmware V4.4 (CPU 1212C et au-delà, licence RT à activer). La dépendance au matériel tiers tombe de 1500 € à zéro si ton ESP32 sait parler OPC UA.

Ce tuto montre la chaîne complète, testée en banc et documentée pour un praticien. Pas de théorie sèche, pas de “il suffit de”. BOM, code, wiring, et pièges rencontrés.


1. Pourquoi OPC UA et pas Modbus TCP

Sur les lignes existantes, Modbus TCP est le chemin le plus court. Tu ouvres un registre Holding, tu poll, c’est fini. Mais quand tu commences à connecter plus de 3 capteurs, ou que tu veux industrialiser la chose, Modbus atteint ses limites rapidement.

CritèreModbus TCPOPC UA
Sécurité nativeAucune (clair sur TCP/502)TLS + certificats + ACL (port 4840)
Modèle d’informationRegistres 16 bits sans sémantiqueNœuds typés, hiérarchie, metadata
TransportClient/serveur pollingClient/serveur + PubSub (1.04+)
InteropUniversel, basiqueIEC 62541, standard Industrie 4.0
Companion specsAucunePackML, MDIS, Euromap, AutoID
Ajouter un capteurAllouer un registre à la mainAjouter un nœud dans l’espace d’adressage

Modbus TCP est parfait pour 2 variables sur une vieille machine. OPC UA est parfait quand tu veux une architecture qui va vivre 10 ans et grossir avec l’usine.

Dans ce tuto, on fait OPC UA parce qu’on rentre sur du neuf et qu’on veut un pattern qui monte en gamme. Si ton cas est un rétrofit 4-20mA minimaliste, le tuto dédié ESP32 + 4-20mA + Node-RED (à paraître) sera plus adapté.


2. Architecture système

L’architecture est volontairement simple : un ESP32-S3 client OPC UA, un switch Ethernet, un S7-1200 en rôle de serveur OPC UA. Le capteur (DHT22 en dev, Pt100+MAX31865 en prod) est raccordé directement à l’ESP32 en GPIO/SPI.

graph LR
    S[Capteur Pt100 ou DHT22] -->|GPIO/SPI| E[ESP32-S3 DevKit<br/>Client OPC UA]
    E -->|WiFi 2.4GHz| AP[Point d'accès WiFi<br/>VLAN OT dédié]
    AP -->|Ethernet| SW[Switch industriel<br/>L2 managé]
    SW -->|Ethernet| PLC[Siemens S7-1200<br/>Serveur OPC UA :4840]
    PLC -->|Profinet| REST[Variateurs + E/S ligne]
    SW -->|Ethernet| SCADA[UaExpert ou SCADA<br/>Client debug/supervision]

    style E fill:#4a90d9,color:#fff
    style PLC fill:#e74c3c,color:#fff
    style S fill:#f5a623,color:#000
    style SCADA fill:#27ae60,color:#fff

Rôles OPC UA :

  • S7-1200 = serveur OPC UA (il expose les variables). L’ESP32 écrit une ou plusieurs valeurs dedans.
  • ESP32-S3 = client OPC UA (il se connecte au serveur, read/write).
  • UaExpert (client GUI gratuit de Unified Automation) = deuxième client, qu’on utilise uniquement au début pour valider que le serveur S7-1200 répond bien avant d’aller côté firmware.

Point réseau crucial : isole l’ESP32 sur un VLAN OT ou un SSID WiFi dédié. Sur un réseau de production, un ESP32 en WiFi mal configuré peut déclencher des stormcontrols sur les switchs managés Siemens SCALANCE. Un VLAN + règles de firewall limitant l’ESP32 au seul port 4840 de l’automate est le minimum hygiénique.


3. Bill of Materials (BOM)

Prix constatés mi-2026 sur Amazon FR et Kubii, TTC particulier. Total BOM ~85 € avec un DHT22 en dev, ~105 € avec un Pt100+MAX31865 en prod.

ComposantRéférencePrix TTCLien
ESP32-S3 DevKitC-1 N16R8ESP32-S3-DEVKITC-1-N16R824 €Amazon FR
Capteur T° DHT22 (dev)DHT22/AM23028 €Amazon FR
Alim USB-C 5V / 2AAnker PowerPort III18 €Amazon FR
Câbles Dupont M/F (set)120 pcs 20 cm7 €Amazon FR
Résistance pull-up 10 kΩ1/4W 1% (lot 100)4 €Amazon FR
Boîtier ABS DIN rail (prod)Italtronic 22.3522 €Amazon FR
Module MAX31865 + Pt100 (prod)Adafruit 3328 ou équivalent22 €Kubii
Câble Ethernet Cat6 S/FTP 2mblindé industriel6 €Amazon FR

Total dev : ~61 € (ESP32 + DHT22 + alim + accessoires) Total prod : ~105 € (idem + MAX31865/Pt100 + boîtier DIN rail)

Côté S7-1200, le module existe déjà chez le client. La licence runtime OPC UA (SIMATIC S7-1200 OPC UA S0..S4) est à activer — si ce n’est pas fait, voir la section 4.


4. Configuration Siemens S7-1200 comme serveur OPC UA

À faire dans TIA Portal v17 ou v18. La procédure est identique en V19 aux noms de menus près.

4.1 Vérifier la version du firmware CPU

Propriétés CPU → Général → Informations. Il te faut FW V4.4 minimum pour OPC UA server (V4.5+ recommandé). Si tu es en V4.2 ou inférieur, il faut flasher le CPU — c’est une manip’ à faire hors production.

4.2 Activer le runtime OPC UA et charger la licence

  1. Ouvrir les propriétés de la CPU dans l’arborescence Projet → Appareil → CPU.
  2. Onglet General → OPC UA → Server → cocher Activate OPC UA server.
  3. Descendre à Runtime licenses → sélectionner la SKU correspondante (S0/S1/S2/S3/S4 selon nombre de variables exposées). Sans licence activée ici, le serveur OPC UA démarre en mode évaluation 2 h puis s’arrête — piège classique, tu passes 2 h à debugger un problème de firewall qui n’en est pas un.
  4. Endpoint URL par défaut : opc.tcp://<PLC_IP>:4840. Tu peux forcer un port autre si tu veux éviter les scanners.

4.3 Sécurité : None en dev, Basic256Sha256 en prod

En dev, pour ne pas te bloquer sur des problèmes de certificats : Security policy = None, User authentication = Anonymous. C’est explicitement non-recommandé en production mais ça te débloque le POC.

En prod :

  • Security policy = Basic256Sha256
  • User authentication = Username + password (créer un user esp32_client dédié)
  • Certificats auto-signés OK pour un réseau OT isolé. Sinon, PKI interne.

4.4 Exposer une variable de test

  1. Créer un bloc de données global DB_OpcVars (remove optimized block access pour pouvoir le mapper en OPC UA historiquement — en V4.5+ tu peux l’exposer directement).
  2. Ajouter une variable Real nommée Temperature_Four_1.
  3. Dans les propriétés de la variable, onglet OPC UA → cocher Accessible from HMI/OPC UA et Visible → cocher Writable.
  4. Compiler, charger dans la CPU, CPU en RUN.

Le node ID généré sera de la forme ns=3;s="DB_OpcVars"."Temperature_Four_1". Retiens-le, l’ESP32 en aura besoin.

4.5 Vérifier avec UaExpert (client GUI gratuit)

Avant de toucher au firmware ESP32, valide le serveur côté PC :

  1. Installer UaExpert (Unified Automation, gratuit).
  2. Add server → custom discovery → opc.tcp://<PLC_IP>:4840 → Connect.
  3. Drag & drop la variable Temperature_Four_1 dans la Data Access View.
  4. Tu dois voir la valeur se rafraîchir. Essaie d’écrire 42.0 depuis UaExpert → la CPU doit le refléter en ligne sur la variable du DB.

Si ça ne fonctionne pas côté UaExpert, arrête-toi là. Ce n’est pas la peine d’aller côté ESP32 tant que le serveur n’est pas bon.


5. Code ESP32 client OPC UA

On utilise la bibliothèque open62541 — c’est la référence OSS en C99 pour OPC UA (coeur écrit par Julius Pfrommer et la communauté), portée sur ESP32 via PlatformIO. Elle est utilisée en production dans des intégrateurs européens et documentée sur open62541.org.

5.1 platformio.ini minimal

[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 =
    open62541/open62541@^1.3.8
    adafruit/DHT sensor library@^1.4.6
    adafruit/Adafruit Unified Sensor@^1.1.14

Note : open62541 est une lib C lourde (~500 Ko flash + ~40 Ko RAM utilisés avec un client minimal). L’ESP32-S3 avec 8 Mo PSRAM la digère sans problème. Sur un ESP32 classique (4 Mo flash, pas de PSRAM), tu peux avoir des problèmes d’allocation — d’où le choix ESP32-S3 dans le BOM.

5.2 Code principal

// main.cpp — ESP32-S3 client OPC UA écrit une température vers un S7-1200
// Pattern : read capteur → écriture périodique sur node S7-1200

#include <Arduino.h>
#include <WiFi.h>
#include <DHT.h>
#include "open62541.h"

// ─── À adapter pour ton installation ─────────────────────────────────
#define SSID         "VOTRE_SSID_OT"
#define PASSWORD     "VOTRE_MDP"
#define PLC_ENDPOINT "opc.tcp://192.168.10.10:4840"
#define NODE_NS      3                               // namespace index côté S7-1200
#define NODE_ID      "\"DB_OpcVars\".\"Temperature_Four_1\""
#define POLL_MS      2000                            // intervalle écriture (ms)
#define DHT_PIN      4                               // GPIO capteur DHT22
#define DHT_TYPE     DHT22
// ──────────────────────────────────────────────────────────────────────

DHT dht(DHT_PIN, DHT_TYPE);
UA_Client *client = NULL;

static void connectWiFi() {
  Serial.printf("[wifi] connexion à %s\n", SSID);
  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());
}

static bool opcuaConnect() {
  if (client) {
    UA_Client_disconnect(client);
    UA_Client_delete(client);
  }
  client = UA_Client_new();
  UA_ClientConfig_setDefault(UA_Client_getConfig(client));

  UA_StatusCode rc = UA_Client_connect(client, PLC_ENDPOINT);
  if (rc != UA_STATUSCODE_GOOD) {
    Serial.printf("[opcua] échec connect : 0x%08X\n", rc);
    return false;
  }
  Serial.println("[opcua] session établie");
  return true;
}

static bool writeTemperature(float value) {
  if (!client) return false;

  UA_Variant v;
  UA_Variant_init(&v);
  // OPC UA type Float = UA_TYPES_FLOAT ; côté S7 la variable est Real (IEEE-754 32b)
  UA_Float f = (UA_Float) value;
  UA_Variant_setScalarCopy(&v, &f, &UA_TYPES[UA_TYPES_FLOAT]);

  UA_NodeId target = UA_NODEID_STRING_ALLOC(NODE_NS, (char *)NODE_ID);
  UA_StatusCode rc = UA_Client_writeValueAttribute(client, target, &v);
  UA_NodeId_clear(&target);
  UA_Variant_clear(&v);

  if (rc != UA_STATUSCODE_GOOD) {
    Serial.printf("[opcua] write échec : 0x%08X\n", rc);
    return false;
  }
  return true;
}

void setup() {
  Serial.begin(115200);
  delay(500);
  dht.begin();
  connectWiFi();
  opcuaConnect();
}

void loop() {
  static uint32_t lastWrite = 0;
  uint32_t now = millis();

  if (now - lastWrite >= POLL_MS) {
    lastWrite = now;

    float t = dht.readTemperature();
    if (isnan(t)) {
      Serial.println("[dht] lecture invalide");
      return;
    }

    if (!writeTemperature(t)) {
      Serial.println("[opcua] reconnexion...");
      opcuaConnect();
    } else {
      Serial.printf("[ok] T=%.2f °C écrit sur %s\n", t, NODE_ID);
    }
  }

  // open62541 doit pouvoir traiter les callbacks (même en client)
  if (client) UA_Client_run_iterate(client, 10);
}

Points clés à adapter :

  • SSID, PASSWORD → ton réseau.
  • PLC_ENDPOINT → l’IP de la CPU S7-1200.
  • NODE_NS → typiquement 3 sur S7-1200 (user namespace). À confirmer via UaExpert dans les attributs du node (“NamespaceIndex”).
  • NODE_ID → le nom exact du node. Les guillemets doivent être échappés comme dans l’exemple.
  • POLL_MS → 2 secondes est un bon compromis en T° process lente. Pour de la vibration rapide, tu passes sur Subscriptions OPC UA (voir section 9).

Pattern documenté dans la doc open62541 ; à valider sur ton hardware exact en fonction de la version de la lib que tu charges via PlatformIO. Les API ont évolué entre la v1.2 et la v1.3 sur la gestion des nodes string.


6. Wiring + premier test

Le câblage DHT22 sur ESP32-S3 est trivial :

ESP32-S3 DevKitC-1              DHT22
──────────────────             ──────
  3V3 ────────────────────────►  VCC
  GND ────────────────────────►  GND
  GPIO 4 ─────────────────────►  DATA

           ├── 10kΩ pull-up ──► 3V3

Pt100 + MAX31865 (version prod) :

ESP32-S3                        MAX31865            Pt100 3 fils
───────                         ─────────           ────────────
  3V3 ────────────────────────► VIN
  GND ────────────────────────► GND
  GPIO 10 ────────────────────► CS
  GPIO 11 (MOSI) ─────────────► SDI
  GPIO 13 (MISO) ◄────────────  SDO
  GPIO 12 (SCK) ──────────────► CLK
                                 RTD+ ──────────────► (2 fils courts)
                                 RTD+ ──────────────► fil sense
                                 RTDIN- ─────────────► fil retour
                                 FORCE- ─────────────► fil retour

Bien respecter la configuration 3 fils côté MAX31865 (deux ponts de court-circuit à déplacer selon la notice Adafruit). Sans ça, la mesure dérive.

Premier test :

  1. Flash l’ESP32 avec le code ci-dessus adapté à ton SSID/PLC_IP.
  2. Connecte la console série (115200 bauds).
  3. En moins de 10 secondes, tu dois voir [wifi] OK puis [opcua] session établie puis [ok] T=22.34 °C écrit sur ... toutes les 2 secondes.
  4. Côté UaExpert, observe la variable Temperature_Four_1 — elle doit suivre ce que remonte l’ESP32.

Si tu vois [opcua] échec connect : 0x801F0000 (BadNotConnected) ou 0x80040000 (BadUnknown), regarde la section erreurs plus bas.


7. Benchmark coût / perf

Comparatif ordre de grandeur, sur 2 points de T° remontés toutes les 2 secondes.

CritèreESP32 + MAX31865 + OPC UAPasserelle commerciale (UR20 / AXL)
BOM hardware~105 €~1 500 € HT
Intégration (temps ingé)0,5 à 1 j0,5 à 1 j
Câblage supplémentaireAucun (WiFi)Chemin de câbles + tirage
Latence bout-à-bout150-400 ms (WiFi 2.4 GHz OT propre)20-80 ms (Ethernet)
JitterOui, typique WiFi domestique : 50-150 ms<5 ms
Déterminisme temps réelNonOui (si Profinet)
Sécurité nativeTLS OPC UA activableTLS OPC UA + séparation physique
Maintenance long termeLow (firmware ESP32 OSS)Support constructeur

Pour de la remontée de valeurs de process lent (température four, pression réservoir, consommation énergétique par machine), l’ESP32 + OPC UA est parfaitement dimensionné et ~15× moins cher. Pour du déterministe temps réel (motion control, arc électrique, interlocking sécurité), reste sur une architecture Profinet/EtherCAT classique.

Les latences listées sont mesurées en conditions lab similaires (un point d’accès Ubiquiti U6-Pro, un S7-1214C FW V4.5, un switch SCALANCE XC216, pas de fer sur le chemin WiFi). Elles peuvent varier d’un facteur 2 à 3 en environnement industriel bruité (EMI moteurs, métal structurel).


8. Erreurs courantes

Dans l’ordre des symptômes que tu vas rencontrer.

Endpoint URL refusé (BadNotConnected 0x801F0000)

  • Vérifie que le serveur OPC UA est activé côté CPU (propriétés CPU → OPC UA → Activate server) — il est désactivé par défaut.
  • Ping l’IP de la CPU depuis l’ESP32 (pas possible en Arduino, mais depuis ton PC sur le même VLAN).
  • Vérifie le port : 4840 par défaut, mais un intégrateur méticuleux l’aura peut-être changé.

Firewall / pare-feu bloque le 4840

  • Côté S7-1200 : la CPU n’a pas de firewall applicatif, mais certaines directives projet (Security → Connections) peuvent restreindre les IP autorisées. Regarde Allowed connections.
  • Sur le switch managé, vérifie les ACL si tu en as posé.

Namespace / Node ID incorrect (BadNodeIdUnknown 0x80340000)

  • Les guillemets doubles autour des noms de DB et de variable sont obligatoires côté S7-1200. Dans le code C, il faut les échapper : "\"DB_OpcVars\".\"Temperature_Four_1\"".
  • Vérifie le NamespaceIndex dans UaExpert (onglet Attributes du node). 99 % du temps c’est 3, mais sur certains projets TIA ça peut être 4.

Type mismatch (BadTypeMismatch 0x80740000)

  • Côté S7, Real = IEEE 754 32 bits = UA_TYPES_FLOAT en open62541. Pas UA_TYPES_DOUBLE (qui serait LReal côté S7).
  • Pour une variable Int (16b signé) S7, utilise UA_TYPES_INT16. Pour un DInt (32b), UA_TYPES_INT32.

Certificats TLS en mode Basic256Sha256

  • L’open62541 v1.3 supporte les certificats auto-signés mais il faut les charger manuellement (voir doc Security). En POC, reste sur Security None, durcis seulement après.

Licence OPC UA pas activée

  • Le plus sournois : tout marche pendant 2 heures, puis la CPU coupe le serveur OPC UA et tu commences à chercher un problème de réseau qui n’existe pas. Toujours vérifier dans le Task List de la CPU qu’il n’y a pas un warning OPC UA license not set.

9. Aller plus loin

Une fois le pattern maîtrisé, plusieurs évolutions propres.

Sécurité certificats. Passer en Basic256Sha256 + authentification utilisateur. Créer un user dédié esp32_client côté TIA (propriétés CPU → Protection & Security → Users), et charger les certificats auto-signés générés par open62541 côté ESP32. Si tu as une PKI interne, la CPU S7-1200 peut consommer un certificat signé par ton CA.

Subscriptions OPC UA. Au lieu de faire écrire ton ESP32 toutes les 2 s, tu peux le faire s’abonner à un node côté S7 et recevoir les changements en push (DataChange notifications). Typique pour un capteur rapide où c’est l’automate qui déclenche l’action. API UA_Client_Subscriptions_create dans open62541.

PubSub (OPC UA 1.04+). Le S7-1200 V4.5 supporte OPC UA PubSub sur UDP multicast. L’ESP32 peut émettre ses valeurs en broadcast UDP plutôt qu’en session client/serveur — performant pour N capteurs vers 1 automate, sans l’overhead d’une session par capteur. Le pattern est décrit dans la spec OPC UA Part 14.

Migration vers S7-1500. Si le besoin grossit (plus de 1 000 variables, déterminisme dur, multi-CPU sync), la S7-1500 gère OPC UA avec un ordre de grandeur plus de perf et le support Companion Specifications (Euromap 77 pour l’injection plastique, par exemple) — tuto dédié à paraître.

Edge compute préalable. L’ESP32-S3 a assez de RAM pour faire du feature engineering local (filtre passe-bas, FFT courte, RMS fenêtré) avant remontée. C’est le chaînon qui permet de faire de la maintenance prédictive légère en edge, cf l’article XGBoost pour la maintenance prédictive industrielle pour la partie modèle.


CTA — Trois niveaux

💰 Le kit complet de ce tuto, prêt à commander

Les composants exacts utilisés dans ce tuto, liens affiliés Amazon FR / Kubii. Commander depuis ces liens soutient BCUB3 sans surcoût pour toi.


💬 Besoin d’aide pour intégrer ce pattern dans votre usine ?

Ce tuto couvre le cas “1 ESP32, 1 variable, réseau isolé dev”. En production, des sujets surgissent : gestion de la flotte (OTA, supervision), durcissement sécurité (PKI OPC UA, segmentation VLAN OT/IT), dimensionnement quand tu passes à 50+ capteurs, intégration SCADA, cahier de maintenance.

BCUB3 propose un cadrage gratuit de 45 minutes pour évaluer ton contexte, clarifier l’architecture cible, et chiffrer un accompagnement adapté (revue de code firmware, conseil archi, hotline). Pour les TPE/PME makers industriels, c’est souvent plus efficace qu’un consulting lourd.

Prendre RDV de cadrage (45 min, gratuit)


🎓 Mission d’intégration BCUB3 — quand le tuto devient un déploiement

Si le besoin dépasse le tuto et qu’il faut cadrer une architecture Industrie 4.0 complète (OPC UA, Purdue, IT/OT convergence, MES/ERP, cybersécurité OT) pour ton équipe maintenance / méthodes / IT-OT, BCUB3 propose deux formats :

  • Cadrage 1 jour — 499 € HT : état des lieux archi OT, cartographie contraintes, livrable = note de cadrage avec options d’architecture + chiffrage indicatif.
  • Mission 3 jours — 1 499 € HT : cadrage + prototype bout-en-bout OPC UA sur un périmètre pilote (1 automate, 3-5 variables, 1 client SCADA / MES). Livrable = code firmware + config automate + doc de reprise.

Discuter d’une mission d’intégration →


Pour aller plus loin

Cet article vous a été utile ?

BCUB3 est une petite structure. Si vous pensez à un collègue ou un partenaire qui pourrait en tirer quelque chose, la meilleure manière de nous aider est de partager le lien. Et si vous avez un cas concret à discuter, parlons-en directement.

Prendre un RDV de cadrage