Aller au contenu principal

ESP32 Ethernet — initialisation, premier flash et OTA

Cette page décrit la cible actuellement prise en charge par leukOS : la WT32-ETH01 v1.4, basée sur un ESP32 et un PHY Ethernet LAN8720. Le firmware est un runtime générique : il ne contient ni le nom de la pièce, ni les identifiants des modules, ni leur câblage. Ces données arrivent ensuite sous forme de JSON, sans reflasher la carte.

Ce qui est flashé, et ce qui ne l'est pas​

ÉlémentTransportFréquence
Runtime générique ESP32UART au premier flash, OTA ensuiteSeulement quand le code du runtime change
Configuration de pièceJSON sur TCP 3233À chaque modification d'une pièce ou d'un module
Commandes et étatsLignes texte sur TCP 23Pendant l'exploitation

Le bouton Envoyer JSON de la page Domotique compile le runtime pour vérifier qu'il reste valide, mais il ne transmet pas le binaire lorsque le contrôleur est configuré avec une URL tcp://.... Il conserve le JSON dans SQLite puis envoie ce même document à l'ESP32.

Prérequis​

  • une carte WT32-ETH01 v1.4 ;
  • un adaptateur USB–UART utilisant une logique 3,3 V ;
  • un câble Ethernet relié au même LAN que le node leukOS ;
  • arduino-cli et le cœur Arduino ESP32 installés ;
  • une alimentation stable adaptée à la carte ou à sa carte porteuse ;
  • les ports TCP 23, 3232 et 3233 accessibles sur le réseau local.

:::danger Électricité et niveaux logiques Les GPIO de l'ESP32 ne tolèrent pas une logique UART à 5 V. Relier les masses, croiser TX et RX, et vérifier l'entrée d'alimentation de la carte porteuse avant la mise sous tension. Les relais secteur doivent être isolés et installés par une personne qualifiée. :::

Câblage du premier flash​

Le premier chargement ne peut pas utiliser OTA, puisque le service OTA n'existe pas encore dans la carte. Il passe donc par l'UART :

Adaptateur USB–UARTWT32-ETH01
TX 3,3 VRXD0
RX 3,3 VTXD0
GNDGND

Pour entrer dans le bootloader série :

  1. couper l'alimentation ;
  2. relier IO0 à GND ;
  3. remettre sous tension ou actionner EN/RESET ;
  4. lancer le téléversement ;
  5. après succès, retirer le pont IO0–GND puis redémarrer normalement.

Le port apparaît généralement comme /dev/ttyUSB0. Le vérifier avant toute commande :

arduino-cli board list
ls -l /dev/ttyUSB*

Si l'accès est refusé, le compte qui lance leukOS ou arduino-cli doit avoir accès au périphérique série, généralement via le groupe dialout.

1. Initialiser le runtime générique​

La source de référence est générée par FirmwareGenerator dans nodes/ui/src/domotics/firmware_generator.cpp. Pour l'obtenir depuis l'IHM :

  1. ouvrir Domotique et créer ou sélectionner la pièce ;
  2. choisir la carte esp32-eth01-v1.4 ;
  3. sélectionner le port série pour le tout premier chargement ;
  4. ouvrir la page Programme ;
  5. utiliser Valider runtime, puis téléverser sur le port série.

Le code commence par fixer le brochage LAN8720 et les trois services réseau :

#include <ArduinoOTA.h>
#include <ETH.h>
#include <Preferences.h>
#include <WiFi.h>
#include <cJSON.h>

#define ETH_PHY_ADDR 1
#define ETH_PHY_POWER 16
#define ETH_PHY_MDC 23
#define ETH_PHY_MDIO 18
#define ETH_PHY_TYPE ETH_PHY_LAN8720
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN

static const IPAddress LEUKOS_STATIC_IP(192, 168, 0, 240);
static const IPAddress LEUKOS_GATEWAY(192, 168, 0, 1);
static const IPAddress LEUKOS_SUBNET(255, 255, 255, 0);
static const uint16_t LEUKOS_COMMAND_PORT = 23;
static const uint16_t LEUKOS_OTA_PORT = 3232;
static const uint16_t LEUKOS_CONFIG_PORT = 3233;

Ce bloc est matériel et réseau. Il est le seul endroit où l'adresse IP par défaut est fixée. Chaque ESP32 du même LAN doit avoir une adresse unique.

Compilation manuelle​

Si le sketch généré a été exporté dans un dossier, il peut être compilé sans le téléverser :

arduino-cli compile \
--fqbn esp32:esp32:esp32 \
--output-dir /tmp/leukos-esp32-output \
--build-path /tmp/leukos-esp32-cache \
/chemin/vers/le/sketch

Le fichier d'application destiné à OTA porte normalement le suffixe .ino.bin. Sur un Raspberry Pi utilisant une carte SD, le premier build peut prendre plusieurs minutes : le cache de compilation doit rester dans /tmp, et seul le .ino.bin utile est conservé durablement.

Premier téléversement manuel par UART​

Après la compilation et avec la carte en mode bootloader :

arduino-cli upload \
--fqbn esp32:esp32:esp32 \
--port /dev/ttyUSB0 \
--input-dir /tmp/leukos-esp32-output \
/chemin/vers/le/sketch

Un effacement complet n'est pas nécessaire à chaque chargement. Il efface aussi la configuration JSON conservée en NVS ; il doit donc rester une opération de diagnostic ou de remise à zéro.

2. Démarrer Ethernet et OTA​

Le runtime attend l'événement ETH_GOT_IP avant d'ouvrir ses serveurs. OTA n'est donc annoncé que lorsque le lien Ethernet est réellement opérationnel :

WiFi.onEvent([](WiFiEvent_t event) {
if (event == ARDUINO_EVENT_ETH_START)
ETH.setHostname("leukos-domotics");

if (event == ARDUINO_EVENT_ETH_DISCONNECTED)
networkReady = false;

if (event == ARDUINO_EVENT_ETH_GOT_IP && !networkReady) {
commandServer.begin();
configServer.begin();
ArduinoOTA.setHostname("leukos-domotics");
ArduinoOTA.setPort(LEUKOS_OTA_PORT);
ArduinoOTA.begin();
networkReady = true;
}
});

ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_MDC, ETH_PHY_MDIO,
ETH_PHY_POWER, ETH_CLK_MODE);
ETH.config(LEUKOS_STATIC_IP, LEUKOS_GATEWAY, LEUKOS_SUBNET);

ArduinoOTA.begin() initialise le service sur le port 3232. Cela ne suffit pas : la boucle principale doit lui rendre la main fréquemment.

void loop() {
if (networkReady) {
ArduinoOTA.handle();
handleCommandClient();
handleConfigClient();
}
}

Éviter les attentes longues et bloquantes dans loop(). Sinon le transfert OTA ralentit, expire ou laisse la carte indisponible pendant la mise à jour.

3. Vérifier le runtime après redémarrage​

Retirer IO0 de la masse, brancher Ethernet et redémarrer. Depuis le node :

python3 - <<'PY'
import socket

with socket.create_connection(("192.168.0.240", 23), timeout=3) as sock:
for command in (b"PING\n", b"INFO\n", b"STATE\n"):
sock.sendall(command)
print(sock.recv(2048).decode().strip())
PY

Une carte saine répond notamment :

PONG
model=esp32-eth01-v1.4 processor=esp32 board=esp32-eth01-v1.4 ota=1 ota_port=3232 config=1 config_port=3233 ...
unconfigured=1

unconfigured=1 est normal juste après le premier flash. Il signifie que le runtime fonctionne mais qu'aucun JSON de pièce n'a encore été reçu.

4. Mettre à jour le firmware par OTA​

OTA sert uniquement quand le code du runtime change. Une modification de prise, lampe, capteur, GPIO ou nom de pièce utilise le port JSON 3233 et ne doit pas envoyer le binaire.

Après une compilation réussie, envoyer seulement l'image d'application :

python3 /opt/leukos-arduino/data/packages/esp32/hardware/esp32/3.0.7/tools/espota.py \
-d \
-i 192.168.0.240 \
-I 192.168.0.9 \
-p 3232 \
-f /tmp/leukos-esp32-output/firmware.ino.bin
  • -i est l'adresse de l'ESP32 ;
  • -I est l'adresse locale du node sur le même réseau ;
  • -p 3232 est le port annoncé par INFO ;
  • -f désigne l'image .ino.bin, pas le bootloader ni la table de partitions.

Le chemin de espota.py dépend de la version du cœur ESP32 installée. Le transfert est terminé uniquement si la commande affiche Result: OK puis Success. Attendre ensuite le redémarrage et refaire PING, INFO, STATE.

Diagnostic rapide​

SymptômeVérification
Aucun port sériecâble de données, alimentation, dmesg, droits sur /dev/ttyUSB0
Failed to connect au premier flashIO0 à la masse, impulsion sur EN, TX/RX croisés
Pas de réponse sur TCP 23lien RJ45, IP unique, sous-réseau, réponse au ping IP
OTA expireport 3232, adresse passée avec -I, pare-feu, appels fréquents à ArduinoOTA.handle()
Téléversement anormalement lentpremière compilation sur SD, cache vide, espace libre et alimentation du Pi
STATE retourne unconfigured=1envoyer le JSON de pièce sur TCP 3233

La suite est décrite dans ESP32 — configuration JSON : format canonique, modules pris en charge et protocole de transfert.