I’m doing a project where I simulate some data on two WT32 ETH01 modules and send the data to an rpi where I store it in an SQLite database. I want to setup up a Modbus master on the rpi and have the WT32s as slaves, sending the data to the rpi, with both WT32 connected to an ethernet switch and then the pi connected to that switch with an ethernet cable. As of now, I’m just testing with one WT32 module. When I run the WT32 code, I get the following:
ETH Started
ETH Connected
Configuring Ethernet with IP, Gateway, Subnet Mask, and DNS...
ETH MAC: x:x:x:x:x:x, IPv4: x.x.x.x (not what it actually said of course)
FULL_DUPLEX, 100Mbps
Waiting for Ethernet connection...
WT32-ETH01 IP Address: x.x.x.x
Starting Modbus server...
Modbus server started successfully
Setting up initial time...
Initialization complete.
Battery Percentage: 85
Timestamp: 2024-6-20 21:58:0
Battery Percentage: 85
Timestamp: 2024-6-20 21:58:1
Battery Percentage: 85
But when I run the master code on the pi I get:
2024-06-23 23:10:26,680 - INFO - Database initialized successfully.
2024-06-23 23:10:29,685 - ERROR - Connection to (x.x.x.x, x) failed: timed out (this is the first WT32)
2024-06-23 23:10:29,685 - ERROR - Failed to connect to x.x.x.x, x
2024-06-23 23:10:32,689 - ERROR - Connection to (x.x.x.x, x) failed: timed out (and this is the second)
2024-06-23 23:10:32,689 - ERROR - Failed to connect to x.x.x.x, x
2024-06-23 23:10:32,689 - ERROR - No clients to run.
Yes, I double checked that the Ip addresses of the slaves matches whats in the master code.
I really have no idea why the master can’t receive the data. Can anyone give me any ideas of what might be going wrong here and where I can go from here? I also don’t believe its a firewall issue since I’ve done other IoT projects in the past on the same network. Thanks.
The WT32 code:
#include <WebServer_WT32_ETH01.h>
#include <ModbusIP_ESP8266.h>
#include <time.h>
const int BATTERY_PERCENTAGE = 100;
const int TIMESTAMP_YEAR = 110;
const int TIMESTAMP_MONTH = 111;
const int TIMESTAMP_DAY = 112;
const int TIMESTAMP_HOUR = 113;
const int TIMESTAMP_MINUTE = 114;
const int TIMESTAMP_SECOND = 115;
ModbusIP mb;
IPAddress myIP("ip address of my WT32 module"); // Unique IP for WT32-ETH01
IPAddress myGW("gateway"); // Gateway
IPAddress mySN("subnet"); // Subnet mask
IPAddress myDNS("dns"); // DNS server
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println("nInitializing...");
Serial.println("Setting up Modbus Server (Slave) on WT32_ETH01...");
Serial.print("Starting Modbus Server (Slave) on WT32_ETH01 with ");
if (!SHIELD_TYPE) {
Serial.println("Error: SHIELD_TYPE is not defined");
return;
}
Serial.println(SHIELD_TYPE);
Serial.println("WebServer version: " + String(WEBSERVER_WT32_ETH01_VERSION));
Serial.println("Setting up WT32_ETH01 event handler...");
WT32_ETH01_onEvent();
Serial.println("Initializing Ethernet...");
if (!ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_POWER, ETH_CLK_MODE)) {
Serial.println("Error: Failed to initialize Ethernet");
return;
}
Serial.println("Configuring Ethernet with IP, Gateway, Subnet Mask, and DNS...");
if (!ETH.config(myIP, myGW, mySN, myDNS)) {
Serial.println("Error: Failed to configure Ethernet");
return;
}
Serial.println("Waiting for Ethernet connection...");
WT32_ETH01_waitForConnect(); // Removed the if condition since it doesn't return a value
Serial.print("WT32-ETH01 IP Address: ");
Serial.println(ETH.localIP());
Serial.println("Starting Modbus server...");
mb.server(); // Start the Modbus server (function does not return a value)
Serial.println("Modbus server started successfully");
Serial.println("Setting up initial time...");
struct tm tm;
tm.tm_year = 2024 - 1900;
tm.tm_mon = 5;
tm.tm_mday = 20;
tm.tm_hour = 21;
tm.tm_min = 58;
tm.tm_sec = 0;
time_t t = mktime(&tm);
if (t == -1) {
Serial.println("Error: Failed to make time");
return;
}
struct timeval now = { .tv_sec = t };
if (settimeofday(&now, NULL) != 0) {
Serial.println("Error: Failed to set time of day");
return;
}
Serial.println("Initialization complete.");
}
void loop() {
if (ETH.linkUp()) {
mb.task();
if (mb.isConnected(IPAddress("same as above"))) {
Serial.println("Master connected");
}
int battery_percentage = 85; // example battery percentage
time_t now;
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Error: Failed to obtain time");
return;
}
time(&now);
localtime_r(&now, &timeinfo);
int year = timeinfo.tm_year + 1900;
int month = timeinfo.tm_mon + 1;
int day = timeinfo.tm_mday;
int hour = timeinfo.tm_hour;
int minute = timeinfo.tm_min;
int second = timeinfo.tm_sec;
Serial.print("Battery Percentage: ");
Serial.println(battery_percentage);
Serial.print("Timestamp: ");
Serial.print(year);
Serial.print("-");
Serial.print(month);
Serial.print("-");
Serial.print(day);
Serial.print(" ");
Serial.print(hour);
Serial.print(":");
Serial.print(minute);
Serial.print(":");
Serial.println(second);
mb.Hreg(BATTERY_PERCENTAGE, battery_percentage);
mb.Hreg(TIMESTAMP_YEAR, year);
mb.Hreg(TIMESTAMP_MONTH, month);
mb.Hreg(TIMESTAMP_DAY, day);
mb.Hreg(TIMESTAMP_HOUR, hour);
mb.Hreg(TIMESTAMP_MINUTE, minute);
mb.Hreg(TIMESTAMP_SECOND, second);
delay(1000);
} else {
Serial.println("Ethernet not connected");
delay(1000);
}
}
The modbus master code on the pi:
import logging
import pymodbus.client as ModbusClient
from pymodbus import ModbusException
import sqlite3
from datetime import datetime
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Initialize the database and create tables if they don't exist
def init_db():
try:
conn = sqlite3.connect('/home/mbus/modbus_project/database/modbus_data.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS battery_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT,
percentage INTEGER)''')
c.execute('''CREATE TABLE IF NOT EXISTS power_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT,
power REAL)''')
conn.commit()
logging.info("Database initialized successfully.")
except sqlite3.Error as e:
logging.error(f"Error initializing database: {e}")
finally:
conn.close()
# Store battery data in the database
def store_battery_data(timestamp, percentage):
try:
conn = sqlite3.connect('/home/mbus/modbus_project/database/modbus_data.db')
c = conn.cursor()
c.execute("INSERT INTO battery_data (timestamp, percentage) VALUES (?, ?)", (timestamp, percentage))
conn.commit()
logging.info(f"Battery data stored successfully: {timestamp}, {percentage}%")
except sqlite3.Error as e:
logging.error(f"Error storing battery data: {e}")
finally:
conn.close()
# Store power data in the database
def store_power_data(timestamp, power):
try:
conn = sqlite3.connect('/home/mbus/modbus_project/database/modbus_data.db')
c = conn.cursor()
c.execute("INSERT INTO power_data (timestamp, power) VALUES (?, ?)", (timestamp, power))
conn.commit()
logging.info(f"Power data stored successfully: {timestamp}, {power} kW")
except sqlite3.Error as e:
logging.error(f"Error storing power data: {e}")
finally:
conn.close()
# Function to read and store battery data from a Modbus client
def read_and_store_battery(client, slave_id):
try:
# Read battery percentage from address 100
rr = client.read_holding_registers(100, 1, unit=slave_id)
if rr.isError():
logging.error(f"Error reading battery percentage: {rr}")
return
battery_percentage = rr.registers[0]
# Read battery timestamp from addresses 110 to 115
rr = client.read_holding_registers(110, 6, unit=slave_id)
if rr.isError():
logging.error(f"Error reading battery timestamp: {rr}")
return
year, month, day, hour, minute, second = rr.registers
battery_timestamp = datetime(year, month, day, hour, minute, second).strftime('%Y-%m-%d %H:%M:%S')
store_battery_data(battery_timestamp, battery_percentage)
except ModbusException as e:
logging.error(f"ModbusException: {e}")
# Function to read and store power data from a Modbus client
def read_and_store_power(client, slave_id):
try:
# Read power generation from addresses 200 and 201
rr = client.read_holding_registers(200, 1, unit=slave_id)
if rr.isError():
logging.error(f"Error reading power generation: {rr}")
return
power_generation_scaled = rr.registers[0]
power_generation = power_generation_scaled / 1000.0 # Scaling back to the original floating point value
# Read power generation timestamp from addresses 210 to 215
rr = client.read_holding_registers(210, 6, unit=slave_id)
if rr.isError():
logging.error(f"Error reading power timestamp: {rr}")
return
year, month, day, hour, minute, second = rr.registers
power_timestamp = datetime(year, month, day, hour, minute, second).strftime('%Y-%m-%d %H:%M:%S')
store_power_data(power_timestamp, power_generation)
except ModbusException as e:
logging.error(f"ModbusException: {e}")
def run_sync_simple_client(clients):
"""Run sync clients for multiple WT32 modules."""
try:
while True:
for client, slave_id, data_type in clients:
if client.is_socket_open():
if data_type == "battery":
read_and_store_battery(client, slave_id)
elif data_type == "power":
read_and_store_power(client, slave_id)
else:
logging.error(f"Connection to {client.host}:{client.port} lost.")
finally:
for client, _, _ in clients:
client.close()
if __name__ == "__main__":
# Initialize the database
init_db()
# Define the WT32 modules (IP address, port, slave ID, and data type)
wt32_modules = [
("xxx.xxx.xxx.xxx", 502, 1, "battery"), # WT32 module for battery data
("xxx.xxx.xxx.xxx", 502, 2, "power") # WT32 module for power data
]
# Create Modbus clients
clients = []
for ip, port, slave_id, data_type in wt32_modules:
client = ModbusClient.ModbusTcpClient(ip, port=port)
if client.connect():
clients.append((client, slave_id, data_type))
logging.info(f"Connected to {ip}:{port}")
else:
logging.error(f"Failed to connect to {ip}:{port}")
# Run the Modbus clients
if clients:
run_sync_simple_client(clients)
else:
logging.error("No clients to run.")
I tried to ping the WT32 module from the pi and it just spit out “host unreachable”.