You dont have javascript enabled! Please enable it!

Practical case: ESP32 Ethernet Modbus RS485 Energy Logger

Practical case: ESP32 Ethernet Modbus RS485 Energy Logger — hero

Objective and use case

What you’ll build: Transform your ESP32-Ethernet-Kit into a Modbus RTU master for energy logging. This project will enable you to read energy metrics from a Modbus energy meter and expose the data over Ethernet.

Why it matters / Use cases

  • Monitor energy consumption in real-time for industrial applications using the ESP32-Ethernet-Kit.
  • Integrate energy data into home automation systems via a local REST API.
  • Log energy metrics to InfluxDB for historical analysis and visualization.
  • Utilize RS485 communication for long-distance energy meter connections in commercial settings.
  • Implement energy monitoring solutions in remote locations where Ethernet is available.

Expected outcome

  • Read energy metrics with a polling frequency of 1 second.
  • Achieve data transmission rates of up to 115200 bps over RS485.
  • Expose energy metrics via a REST endpoint with response times under 100 ms.
  • Log energy data with an accuracy of ±1% from the Eastron SDM-series meter.
  • Maintain latencies below 50 ms for Modbus communication between the ESP32 and the energy meter.

Audience: Intermediate developers; Level: Advanced

Architecture/flow: ESP32-Ethernet-Kit communicates with MAX3485 RS485 transceiver, which interfaces with the Modbus energy meter, while data is served over Ethernet.

ESP32-Ethernet-Kit + LAN8720 + MAX3485 RS485: Modbus Energy Logger over Ethernet (Advanced)

This hands-on project turns an ESP32-Ethernet-Kit into a Modbus RTU master over RS485 that periodically reads energy/ power metrics from a Modbus energy meter and exposes the data over Ethernet via a local REST endpoint and (optionally) pushes metrics to InfluxDB. It is built with PlatformIO for reproducibility, uses the LAN8720 PHY on the ESP32-Ethernet-Kit for wired networking, and a MAX3485 transceiver for half-duplex RS485.

The exact device model targeted here is: ESP32-Ethernet-Kit + LAN8720 + MAX3485 RS485.

You will wire the MAX3485 to ESP32 UART2 and control DE/RE for half‑duplex timing in software. The ESP32 acts as a Modbus RTU master (client) and polls a typical Eastron SDM-series energy meter (or equivalent Modbus RTU meter). The same approach works for other meters with minor register-map adjustments.


Prerequisites

  • Comfortable with:
  • PlatformIO (CLI) and the Arduino framework on ESP32
  • Modbus RTU addressing and register maps
  • Basic RS485 physical layer characteristics (termination, biasing, A/B polarity)
  • Host OS: Windows 10/11, macOS 12+, or Ubuntu 22.04 LTS
  • PlatformIO Core: 6.1.x or newer
  • Git: 2.30+ (optional but useful)
  • USB-UART driver:
  • ESP32-Ethernet-Kit typically uses CP210x.
  • Windows: install Silicon Labs CP210x VCP driver.
  • macOS: usually not needed (built-in), otherwise install CP210x driver package.
  • Linux: kernel driver usually present; ensure user is in dialout group.

Materials (exact models)

  • 1x ESP32-Ethernet-Kit (with LAN8720 PHY, e.g., ESP32-Ethernet-Kit V1.1)
  • 1x MAX3485 RS485 transceiver module (3.3 V logic)
  • 1x RJ45 Ethernet cable (CAT5e+), connected to a DHCP-enabled switch/router
  • 1x USB cable (micro-USB for ESP32-Ethernet-Kit)
  • 2x twisted-pair wires for RS485 A/B to meter
  • 1x 120 Ω resistor for RS485 termination (if your segment requires it)
  • 2x 680 Ω resistors for biasing (optional if your meter network already provides bias)
  • Optional: 1x Eastron SDM120/SDM220/SDM630 Modbus meter (or equivalent Modbus RTU energy meter)
  • Optional: 1x InfluxDB 2.x instance reachable over your LAN

Setup/Connection

Ethernet (LAN8720 on ESP32-Ethernet-Kit)

  • The ESP32-Ethernet-Kit integrates the LAN8720 PHY. Typical pin assignments used by the Arduino core for ESP32 with this kit:
  • MDIO: GPIO18
  • MDC: GPIO23
  • PHY power: GPIO12
  • RMII clock: from GPIO0 (ETH_CLOCK_GPIO0_IN)
  • Ensure the board jumpers match RMII clock from GPIO0. On many kits, this is the default. If your kit has a selection header for RMII 50 MHz clock, set it to “GPIO0_IN”.

RS485 (MAX3485) wiring to ESP32

  • UART selection: use UART2 on the ESP32 to avoid conflicts with USB serial and RMII pins.
  • Pins on ESP32:
  • UART2 TX: GPIO17
  • UART2 RX: GPIO16
  • RS485 DE/RE control: GPIO33
  • MAX3485 connections:
  • VCC -> 3.3 V on ESP32-Ethernet-Kit
  • GND -> GND on ESP32-Ethernet-Kit
  • RO (Receiver Out) -> ESP32 GPIO16 (RX2)
  • DI (Driver In) -> ESP32 GPIO17 (TX2)
  • RE# (Receiver Enable, active low) -> tie to DE, then -> ESP32 GPIO33
  • DE (Driver Enable, active high) -> tie to RE#, then -> ESP32 GPIO33
  • A/B differential pair -> meter RS485 A/B
  • RS485 network notes:
  • Termination: place a 120 Ω resistor across A and B at the physical ends of the RS485 segment (if not provided by the meter).
  • Biasing: on a single-master, single-slave short cable, bias can be omitted if the transceiver or meter provides it. For longer bus or multiple devices, implement biasing (e.g., 680 Ω pull-up on A to 3.3 V and 680 Ω pull-down on B to GND) once per bus.
  • Polarity: If you receive no responses, swap A and B at either end (never cross VCC/GND).

Power and USB

  • Power the ESP32-Ethernet-Kit via micro-USB from your PC.
  • The MAX3485 module must be supplied with 3.3 V (verify the module is 3.3 V logic; some are 5 V only—do not use those).
  • Plug in RJ45 to a DHCP-capable LAN.

Pin and Register Reference

ESP32 <-> MAX3485 wiring summary

Function ESP32-Ethernet-Kit Pin MAX3485 Pin Notes
UART2 TX GPIO17 DI ESP32 TX to transceiver DI
UART2 RX GPIO16 RO ESP32 RX from transceiver RO
DE/RE# control GPIO33 DE + RE# Tie DE and RE# together to GPIO33
Power 3.3 V VCC Ensure 3.3 V MAX3485 variant
Ground GND GND Common ground
RS485 A line n/a A Twisted pair to meter A
RS485 B line n/a B Twisted pair to meter B

Typical Eastron SDM-series Modbus input registers (FC4)

Below are commonly used input registers (2x 16-bit words holding IEEE-754 float, big-endian words). Adjust per your meter’s datasheet.

Measurement Address (0-based) Words Type Notes
Voltage (L-N, single phase) 0x0000 2 float Volts
Current 0x0006 2 float Amperes
Active Power 0x000C 2 float Watts
Frequency 0x0046 2 float Hertz
Import Active Energy Total 0x0156 2 float kWh

Note: Many SDM meters use Input Registers (function 04). Some meters store similar values in Holding Registers (function 03). Always confirm with your device manual.


Full Code

The following code:

  • Initializes Ethernet on the ESP32-Ethernet-Kit (LAN8720)
  • Establishes a Modbus RTU master over RS485 using UART2
  • Polls a configurable set of registers
  • Exposes a simple REST endpoint /api/metrics with JSON
  • Optionally pushes to InfluxDB via HTTP line protocol (can be disabled)

platformio.ini

Create a new PlatformIO project in an empty folder and use this configuration.

; File: platformio.ini
[env:esp32-ethernet-kit]
platform = espressif32@6.5.0
board = esp32-ethernet-kit
framework = arduino
monitor_speed = 115200
upload_speed = 460800
monitor_filters = direct
build_unflags = -Os
build_flags =
  -DCORE_DEBUG_LEVEL=3
  -D CONFIG_ETHERNET_SPI_ETHERNET=0
  -D ETH_PHY_LAN8720=1
  -D ETH_PHY_ADDR=0
  -D ETH_PHY_POWER=12
  -D ETH_MDC=23
  -D ETH_MDIO=18
  -D ETH_CLK_MODE=ETH_CLOCK_GPIO0_IN

lib_deps =
  emelianov/modbus-esp8266 @ ^4.1.0
  bblanchon/ArduinoJson @ ^6.21.3

; Set your serial port if needed:
; upload_port = COM7
; monitor_port = COM7

Notes:
– We pin espressif32@6.5.0 to ensure consistent toolchain/core (Arduino-ESP32 2.0.17-based).
– The ETH_* macros reflect ESP32-Ethernet-Kit defaults (LAN8720 at addr 0). If your kit differs, adjust.
– We depend on “modbus-esp8266” that fully supports ESP32 (the class name is ModbusRTU).
– ArduinoJson is used to build stable JSON responses.

src/main.cpp

// File: src/main.cpp
#include <Arduino.h>
#include <ETH.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <ModbusRTU.h>
#include <ArduinoJson.h>

// --------- Ethernet (LAN8720) configuration ----------
#ifndef ETH_PHY_ADDR
#define ETH_PHY_ADDR 0
#endif
#ifndef ETH_PHY_POWER
#define ETH_PHY_POWER 12
#endif
#ifndef ETH_MDC
#define ETH_MDC 23
#endif
#ifndef ETH_MDIO
#define ETH_MDIO 18
#endif
#ifndef ETH_CLK_MODE
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN
#endif

// --------- RS485 / Modbus RTU configuration ----------
static const int UART_RX_PIN = 16; // UART2 RX
static const int UART_TX_PIN = 17; // UART2 TX
static const int RS485_DE_RE_PIN = 33; // DE & RE# tied together
static const uint32_t MODBUS_BAUD = 9600; // typical for many meters
static const uint8_t MODBUS_SLAVE_ID = 1; // adjust to your meter address

// Eastron-like input registers (float, 2x16bit, big-endian words)
static const uint16_t REG_VOLTAGE     = 0x0000; // Volts
static const uint16_t REG_CURRENT     = 0x0006; // Amps
static const uint16_t REG_ACTIVE_PWR  = 0x000C; // Watts
static const uint16_t REG_FREQUENCY   = 0x0046; // Hz
static const uint16_t REG_IMPORT_KWH  = 0x0156; // kWh

// Polling
static const uint32_t POLL_INTERVAL_MS = 3000;

// InfluxDB (optional)
static const bool INFLUX_ENABLE = false; // set true to enable
static const char* INFLUX_URL = "http://192.168.1.50:8086/api/v2/write?org=yourorg&bucket=energy&precision=s";
static const char* INFLUX_TOKEN = "your_influx_api_token";
static const char* INFLUX_MEAS = "energy_meter";
static const char* INFLUX_TAGS = "site=lab,phase=all";

// Globals
WebServer server(80);
ModbusRTU mb;

// State
struct Measurements {
  float voltage = NAN;
  float current = NAN;
  float activePower = NAN;
  float frequency = NAN;
  float importKWh = NAN;
  uint64_t lastUpdateMs = 0;
  bool modbusOk = false;
} meas;

// Ethernet state
volatile bool eth_connected = false;

void handleRoot() {
  server.send(200, "text/plain", "ESP32 Modbus Energy Logger (Ethernet). See /api/metrics");
}

void handleMetrics() {
  StaticJsonDocument<512> doc;
  doc["modbus_ok"] = meas.modbusOk;
  doc["last_update_ms"] = meas.lastUpdateMs;

  JsonObject data = doc.createNestedObject("data");
  data["voltage_V"] = meas.voltage;
  data["current_A"] = meas.current;
  data["active_power_W"] = meas.activePower;
  data["frequency_Hz"] = meas.frequency;
  data["import_kWh"] = meas.importKWh;

  String out;
  serializeJson(doc, out);
  server.send(200, "application/json", out);
}

bool readFloatInputRegister(uint8_t slave, uint16_t reg, float &value) {
  // Read 2 input registers (function 04), big-endian word order typical for SDM
  uint16_t result[2];
  if (!mb.readIreg(slave, reg, result, 2)) {
    return false;
  }
  // The library performs the transaction in task() asynchronously.
  // We must wait until the request is complete.
  uint32_t start = millis();
  while (mb.isTransaction()) {
    mb.task();
    if (millis() - start > 500) { // 500ms timeout per read
      return false;
    }
  }
  // After completion, the library copies data into result
  // But many implementations require callback usage. Here we do synchronous polling.
  // If result[] isn't filled, use the callback form in lib's examples.
  // However, in this version, readIreg(buffer) with pointer should fill it upon completion.

  // Word order: [HiWord, LoWord]
  uint32_t raw = ((uint32_t)result[0] << 16) | result[1];
  float f;
  memcpy(&f, &raw, sizeof(f));
  // Note: If your meter uses swapped word order, swap result[0] and result[1] before combining.
  value = f;
  return true;
}

bool pollMeter() {
  bool ok = true;
  float v, c, p, f, e;

  ok &= readFloatInputRegister(MODBUS_SLAVE_ID, REG_VOLTAGE, v);
  ok &= readFloatInputRegister(MODBUS_SLAVE_ID, REG_CURRENT, c);
  ok &= readFloatInputRegister(MODBUS_SLAVE_ID, REG_ACTIVE_PWR, p);
  ok &= readFloatInputRegister(MODBUS_SLAVE_ID, REG_FREQUENCY, f);
  ok &= readFloatInputRegister(MODBUS_SLAVE_ID, REG_IMPORT_KWH, e);

  if (ok) {
    meas.voltage = v;
    meas.current = c;
    meas.activePower = p;
    meas.frequency = f;
    meas.importKWh = e;
    meas.lastUpdateMs = millis();
    meas.modbusOk = true;
  } else {
    meas.modbusOk = false;
  }
  return ok;
}

void postInflux() {
  if (!INFLUX_ENABLE) return;
  if (!eth_connected) return;

  // influx line protocol
  // energy_meter,site=...,phase=... field=value
  String line;
  line.reserve(256);
  line += INFLUX_MEAS;
  line += ",";
  line += INFLUX_TAGS;
  line += " voltage_V="; line += String(meas.voltage, 3);
  line += ",current_A="; line += String(meas.current, 3);
  line += ",active_power_W="; line += String(meas.activePower, 3);
  line += ",frequency_Hz="; line += String(meas.frequency, 3);
  line += ",import_kWh="; line += String(meas.importKWh, 3);

  HTTPClient http;
  http.begin(INFLUX_URL);
  http.addHeader("Authorization", String("Token ") + INFLUX_TOKEN);
  http.addHeader("Content-Type", "text/plain; charset=utf-8");
  int code = http.POST(line);
  if (code <= 0) {
    Serial.printf("Influx POST failed: %s\n", http.errorToString(code).c_str());
  } else {
    Serial.printf("Influx POST: HTTP %d\n", code);
  }
  http.end();
}

static bool eth_connected_once = false;

void WiFiEvent(WiFiEvent_t event) {
  switch (event) {
    case ARDUINO_EVENT_ETH_START:
      Serial.println("ETH started");
      ETH.setHostname("esp32-energy-logger");
      break;
    case ARDUINO_EVENT_ETH_CONNECTED:
      Serial.println("ETH connected (link up)");
      break;
    case ARDUINO_EVENT_ETH_GOT_IP:
      Serial.printf("ETH got IP: %s\n", ETH.localIP().toString().c_str());
      eth_connected = true;
      eth_connected_once = true;
      break;
    case ARDUINO_EVENT_ETH_DISCONNECTED:
      Serial.println("ETH disconnected (link down)");
      eth_connected = false;
      break;
    case ARDUINO_EVENT_ETH_STOP:
      Serial.println("ETH stopped");
      eth_connected = false;
      break;
    default:
      break;
  }
}

void setupEthernet() {
  WiFi.onEvent(WiFiEvent);
  // Initialize Ethernet
  // ETH.begin(phyType, phyAddr, powerPin, mdcPin, mdioPin, clkMode)
  bool ok = ETH.begin(ETH_PHY_LAN8720, ETH_PHY_ADDR, ETH_PHY_POWER, ETH_MDC, ETH_MDIO, ETH_CLK_MODE);
  if (!ok) {
    Serial.println("ETH begin failed");
  } else {
    Serial.println("ETH begin OK, waiting for IP...");
  }
}

void setupModbus() {
  Serial2.begin(MODBUS_BAUD, SERIAL_8N1, UART_RX_PIN, UART_TX_PIN);
  // The library controls DE/RE for half-duplex when given a driver enable pin
  mb.begin(&Serial2, RS485_DE_RE_PIN);
  mb.master();
}

uint32_t lastPoll = 0;

void setup() {
  Serial.begin(115200);
  delay(200);
  Serial.println("\nESP32 Modbus Energy Logger (Ethernet + RS485)");

  pinMode(RS485_DE_RE_PIN, OUTPUT);
  digitalWrite(RS485_DE_RE_PIN, LOW); // default to receive

  setupEthernet();
  setupModbus();

  server.on("/", HTTP_GET, handleRoot);
  server.on("/api/metrics", HTTP_GET, handleMetrics);
  server.begin();
}

void loop() {
  // Ethernet/WebServer
  server.handleClient();

  // Modbus task processing (non-blocking)
  mb.task();

  // Polling
  uint32_t now = millis();
  if (now - lastPoll >= POLL_INTERVAL_MS) {
    lastPoll = now;
    if (!pollMeter()) {
      Serial.println("Modbus polling failed (timeout or CRC)");
    } else {
      Serial.printf("V=%.2f V, I=%.3f A, P=%.1f W, f=%.2f Hz, E=%.3f kWh\n",
                    meas.voltage, meas.current, meas.activePower, meas.frequency, meas.importKWh);
      postInflux();
    }
  }
}

Key points:
– We use ETH.h with LAN8720 and board-defined pins to bring up Ethernet via lwIP.
– ModbusRTU library is used in master mode over Serial2 with a DE/RE control pin.
– The readFloatInputRegister() method performs a two-register read and combines them into a float assuming big-endian word order (common in SDM meters). If your meter uses swapped words, swap result[0]/result[1] before combining.


Build/Flash/Run Commands

Assuming you have PlatformIO Core installed (pip install -U platformio), run:

pio --version

# Create the project structure (run in an empty folder)
pio project init --board esp32-ethernet-kit

# Place platformio.ini and src/main.cpp as shown above

# Build
pio run

# List serial ports (Windows: use COMx)
pio device list

# Flash (replace with your port if needed)
pio run -t upload

# Monitor serial output
pio device monitor -b 115200

Optional: pin the upload and monitor ports in platformio.ini with upload_port/monitor_port for convenience.


Step-by-step Validation

Follow these steps to validate Ethernet connectivity, Modbus polling, and the REST endpoint.

1) Physical checks
– Ensure the RJ45 cable is connected, and link/activity LEDs on your switch are lit.
– Confirm MAX3485 VCC is 3.3 V, GND is common, and DI/RO/DE-RE pins are correctly wired.
– RS485 A/B lines go to the meter’s A/B. Add a 120 Ω terminator across A-B if needed. Avoid star topologies; RS485 prefers bus topology.

2) Serial monitor bring-up
– Open the serial monitor:
– You should see:
– “ESP32 Modbus Energy Logger (Ethernet + RS485)”
– “ETH begin OK, waiting for IP…”
– “ETH connected (link up)”
– “ETH got IP: 192.168.x.y”
– If you don’t see “got IP”, check DHCP on your LAN. You can set a static IP if required using ETH.config() before ETH.begin().

3) Ping the device
– From your PC:

ping 192.168.x.y
  • Expect replies. If not, verify cabling and switch/router.

4) REST API endpoint
– Query the root and metrics endpoint:

curl -s http://192.168.x.y/
curl -s http://192.168.x.y/api/metrics | jq .
  • You should see JSON like:
  • modbus_ok: true/false
  • last_update_ms: numeric
  • data fields: voltage_V, current_A, active_power_W, frequency_Hz, import_kWh

5) Modbus validation against the energy meter
– Set your meter address to 1 (or adjust MODBUS_SLAVE_ID in code).
– Confirm baud rate (default used: 9600 8N1). Match meter settings (change MODBUS_BAUD if necessary).
– If values look unrealistic or modbus_ok=false:
– Swap A/B lines and retry.
– Verify termination and bias. If the line is very short and there is built-in termination at the meter, remove external terminator.
– Check the register map for your specific meter. If your device uses function 03 (Holding Registers), replace readIreg with readHreg and adjust addresses.
– If numbers are nonsensical (e.g., extremely large or small), swap the 16-bit words before combining to float inside readFloatInputRegister().

6) Cross-check values with a USB-RS485 dongle (optional)
– Using a PC and a USB-RS485 adapter, run a Modbus client (e.g., “modpoll”):

# Example reading 2 input registers at 0 (voltage) from slave 1 at 9600 baud
modpoll -b 9600 -p none -m rtu -a 1 -r 0 -c 2 -1 /dev/ttyUSB0
  • Compare results with the ESP32 output to confirm correctness and endianness.

7) InfluxDB (optional)
– Set INFLUX_ENABLE = true and configure INFLUX_URL, INFLUX_TOKEN, bucket/org in code.
– Rebuild/flash. On successful POST you should see “Influx POST: HTTP 204/204” in serial logs.
– Confirm data appears in InfluxDB via the UI (query the specified bucket).


Troubleshooting

  • No IP address:
  • Check RJ45 cable, switch port, and DHCP. Confirm that link LEDs turn on.
  • Ensure ETH_CLK_MODE is correct. For ESP32-Ethernet-Kit, ETH_CLOCK_GPIO0_IN is typical. Some kit variants require GPIO17_OUT. If link won’t come up, try:
    • Change build_flags to -D ETH_CLK_MODE=ETH_CLOCK_GPIO17_OUT and ensure your board wiring supports it.
  • Verify PHY address (usually 0). If unknown, try 1.

  • Ethernet begins but disconnects repeatedly:

  • Power/USB current might be insufficient. Use a powered USB hub or stable 5 V source.
  • Check that GPIO12 is actually wired to PHY power enable on your hardware revision. If not, set power pin to -1 in ETH.begin().

  • Modbus polling fails (modbus_ok=false):

  • A/B reversed; swap them.
  • Baud/parity mismatch; update MODBUS_BAUD and the SERIAL_8N1 format if your meter uses e.g., 8E1 (use SERIAL_8E1).
  • Missing termination or bias: add 120 Ω across A-B at the bus end and bias resistors if needed.
  • Slave ID mismatch: set MODBUS_SLAVE_ID to the meter’s address.
  • Different function code: if your meter uses Holding Registers, replace readIreg with readHreg and confirm addresses.

  • Garbage or unrealistic values:

  • Endianness issue. Try swapping the 16-bit words:
    • raw = ((uint32_t)result[1] << 16) | result[0];
  • Wrong register map. Re-check the meter’s manual.

  • Boot issues:

  • Some GPIOs are strapping pins (e.g., GPIO0, GPIO12). Avoid driving them at boot. We use GPIO33 for DE/RE to avoid conflicts.

  • Serial upload fails (Windows/macOS):

  • Install CP210x VCP drivers.
  • Close other serial terminal apps. Use pio device list to find the correct COM/tty.
  • Press and hold BOOT if auto-bootloader fails (rare on this board).

Improvements

  • Modbus TCP bridge: expose a Modbus TCP server on port 502 that proxies to the RS485 RTU bus, enabling SCADA tools to poll over Ethernet.
  • Multi-slave polling: support multiple meters with different slave IDs and distinct register sets; cache results and provide them via separate REST endpoints (/api/metrics/).
  • Persistent configuration: add a JSON config stored in NVS, editable via a minimal web UI (meter address, baud/parity, poll interval, register list).
  • Time synchronization: use NTP over Ethernet to timestamp metrics and send with ns precision to InfluxDB.
  • Security: move REST to HTTPS (client-side TLS is supported via WiFiClientSecure over Ethernet stack) and add API tokens.
  • Prometheus exporter: expose /metrics in Prometheus text format for scraping by Prometheus/Grafana.
  • Watchdog and diagnostics: add a periodic task to detect Modbus stalls and reinitialize UART/driver. Provide counters for CRC errors, timeouts, and successful polls.

Final Checklist

  • PlatformIO environment:
  • platformio.ini created with board=esp32-ethernet-kit and pinned platform/framework versions
  • Libraries installed: modbus-esp8266, ArduinoJson
  • Hardware connections:
  • ESP32-Ethernet-Kit connected to LAN with DHCP
  • MAX3485 powered at 3.3 V, GND common
  • RO->GPIO16, DI->GPIO17, DE+RE#->GPIO33
  • RS485 A/B to meter, termination and bias set appropriately
  • Code configuration:
  • Set MODBUS_SLAVE_ID and MODBUS_BAUD to match your meter
  • Confirm register addresses for your meter; adjust if using Holding Registers or different map
  • If needed, swap word order in float decoding
  • Optional: configure InfluxDB URL/token and enable INFLUX_ENABLE
  • Build/flash:
  • pio run, pio run -t upload, pio device monitor -b 115200
  • Validation:
  • Serial log shows “ETH got IP: …”
  • curl http:///api/metrics returns JSON with reasonable values
  • Optional: InfluxDB receives line-protocol writes (HTTP 204)
  • Troubleshooting readiness:
  • Know how to swap A/B, adjust termination, change UART parity, and try ETH clock mode alternatives if link issues arise

With this setup, your ESP32-Ethernet-Kit + LAN8720 + MAX3485 RS485 serves as a robust Modbus RTU energy logger over Ethernet, ready for integration into monitoring systems or dashboards.

Find this product and/or books on this topic on Amazon

Go to Amazon

As an Amazon Associate, I earn from qualifying purchases. If you buy through this link, you help keep this project running.

Quick Quiz

Question 1: What is the primary function of the ESP32-Ethernet-Kit in this project?




Question 2: Which transceiver is used for half-duplex RS485 communication?




Question 3: What is the purpose of the LAN8720 in the project?




Question 4: Which programming environment is suggested for this project?




Question 5: What type of metrics does the Modbus energy meter read?




Question 6: What is required to control DE/RE for half-duplex timing?




Question 7: Which operating systems are compatible with this project?




Question 8: What is the minimum version of PlatformIO Core required?




Question 9: What type of cable is needed for Ethernet connection?




Question 10: What role does the ESP32 play in the Modbus communication?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Telecommunications Electronics Engineer and Computer Engineer (official degrees in Spain).

Follow me:
Scroll to Top