Push POE project

This commit is contained in:
naab
2025-05-29 11:06:22 +07:00
parent 286ef3e7fe
commit ad87c01f34
12 changed files with 317 additions and 0 deletions

Binary file not shown.

Binary file not shown.

20
config.py Normal file
View File

@ -0,0 +1,20 @@
import time
# Modbus configuration
MODBUS_HOST = "10.84.48.153"
MODBUS_PORT = 505
UNIT_ID = 1
# MQTT configuration
MQTT_BROKER = "mqtt.service.mesh"
MQTT_PORT = 1883
MQTT_TOPIC = "Temperature_Humidity"
MQTT_CLIENT_ID = f"modbus-mqtt-client-{int(time.time())}"
MQTT_USERNAME = "relay"
MQTT_PASSWORD = "Sey@K9c&Q4^"
# Location information
LOCATION = "Office"
# Read and publish cycle configuration (seconds)
PUBLISH_INTERVAL = 10

17
main.py Normal file
View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
"""
Modbus to MQTT Bridge Service
This service reads temperature and humidity data from a Modbus TCP server
and publishes the data to an MQTT broker.
Usage:
python main.py
Author: POE Project
"""
from sensor_bridge import main_loop
if __name__ == "__main__":
main_loop()

View File

@ -0,0 +1,17 @@
# Active Context
## Current Work Focus
- The script is stable and functional, continuously reading temperature and humidity from a Modbus TCP device and publishing the data to an MQTT broker in pretty-printed JSON format.
- The main focus is on reliability, error handling, and clear logging.
## Recent Changes
- Improved JSON formatting for MQTT payloads (pretty print with indent).
- Refined temperature calibration logic (subtracting 40 from raw value after scaling).
- Enhanced logging for all major events and error conditions.
- All configuration is now at the top of the script for easier modification.
## Next Steps
- Optional: Parameterize configuration via environment variables or a config file for easier deployment.
- Optional: Add support for additional Modbus registers or sensors.
- Optional: Implement more advanced error recovery or alerting (e.g., email/SMS on repeated failures).
- Optional: Containerize the application for easier deployment in production environments.

View File

@ -0,0 +1,20 @@
# Product Context
## Why This Project Exists
Many industrial and environmental sensors use the Modbus protocol for data communication, while modern IoT and monitoring systems often rely on MQTT for data ingestion and distribution. This project bridges the gap between legacy Modbus devices and MQTT-based platforms, enabling seamless integration and real-time data flow.
## Problems Solved
- Eliminates the need for manual data collection from Modbus sensors.
- Automates the process of converting and forwarding sensor data to cloud or local MQTT brokers.
- Provides a reliable, scriptable, and extensible solution for integrating Modbus sensors into IoT ecosystems.
## How It Should Work
- The script runs continuously, connecting to a Modbus TCP device to read temperature and humidity data at regular intervals.
- Data is processed and formatted as JSON, then published to a specified MQTT topic.
- The system handles connection issues gracefully, with logging for troubleshooting.
## User Experience Goals
- Simple configuration via code variables (host, port, credentials, topic, etc.).
- Clear, timestamped logging for all major events and errors.
- Data is published in a structured, readable JSON format for easy consumption by MQTT subscribers.
- Minimal setup required; designed for headless/server environments.

23
memory-bank/progress.md Normal file
View File

@ -0,0 +1,23 @@
# Progress
## What Works
- The script successfully connects to both the Modbus TCP device and the MQTT broker.
- Temperature and humidity data are read from the correct Modbus registers and calibrated as per device documentation.
- Data is published to the MQTT topic in a pretty-printed JSON format.
- Logging provides clear feedback on all major events and errors.
- The script handles connection failures and retries in the main loop.
## What's Left to Build
- (Optional) Configuration via environment variables or external config file.
- (Optional) Support for additional sensors or Modbus registers.
- (Optional) Advanced error notification (e.g., email/SMS alerts).
- (Optional) Dockerfile or deployment scripts for production use.
## Current Status
- The project is functional and meets its core requirements for a Modbus-to-MQTT bridge.
- Ready for deployment in environments matching the current configuration.
## Known Issues
- All configuration is hardcoded; not ideal for dynamic or multi-environment deployments.
- No persistent storage or buffering if MQTT broker is temporarily unavailable.
- No web UI or REST API for monitoring or control.

View File

@ -0,0 +1,25 @@
# Project Brief
## Project Name
Modbus-to-MQTT Bridge for Environmental Sensor
## Overview
This project implements a Python-based bridge that reads temperature and humidity data from a Modbus TCP device and publishes the readings to an MQTT broker in JSON format. The system is designed for continuous, automated data acquisition and integration with IoT or monitoring platforms.
## Core Requirements
- Connect to a Modbus TCP device (sensor gateway) to read holding registers for temperature and humidity.
- Process and calibrate the raw sensor data as per device documentation.
- Publish the processed data to a specified MQTT topic at a configurable interval.
- Support MQTT authentication (username/password).
- Provide clear logging for connection, data acquisition, and publishing events.
## Goals
- Enable seamless integration of Modbus-based sensors with MQTT-based IoT systems.
- Ensure reliable, periodic data transfer with error handling and reconnection logic.
- Maintain code clarity and extensibility for future enhancements (e.g., more sensors, additional data fields).
## Scope
- Single Python script (poe.py) as the main application.
- No web interface or GUI; headless operation via command line.
- Focus on environmental data (temperature, humidity) but extensible for other Modbus registers.
- No persistent storage; data is transient and only sent to MQTT.

View File

@ -0,0 +1,21 @@
# System Patterns
## System Architecture
- **Single-process, event-driven script**: The application runs as a single Python process, using a main loop to periodically read from Modbus and publish to MQTT.
- **Polling pattern**: Data is acquired from the Modbus device at a fixed interval (configurable via `PUBLISH_INTERVAL`).
- **Bridge pattern**: The script acts as a bridge between two protocols (Modbus TCP and MQTT), translating and forwarding data.
## Key Technical Decisions
- **Direct variable configuration**: All connection and operational parameters are set as variables at the top of the script for simplicity.
- **Error handling and reconnection**: The script checks and re-establishes connections to both Modbus and MQTT as needed, with logging for all failures.
- **JSON formatting**: Data is published in pretty-printed JSON for readability and ease of integration.
- **Separation of concerns**: Reading/publishing logic is encapsulated in a dedicated function (`read_and_publish_data`), while the main loop handles orchestration and lifecycle.
## Design Patterns
- **Callback pattern**: Used for MQTT events (on_connect, on_publish).
- **Try/except/finally**: Used for robust error handling and resource cleanup.
- **Headless operation**: No UI; all feedback is via logs and MQTT messages.
## Component Relationships
- The Modbus client and MQTT client are instantiated and managed independently, but coordinated within the main loop.
- Data flows from Modbus -> Python processing -> MQTT publish.

View File

@ -0,0 +1,21 @@
# Technical Context
## Technologies Used
- **Python 3.x**: Main programming language.
- **pymodbus**: For Modbus TCP client communication.
- **paho-mqtt**: For MQTT client functionality.
- **Logging**: Python's built-in logging module for event and error tracking.
## Dependencies
- `pymodbus` (>=2.5, <4.0 recommended for current code)
- `paho-mqtt`
## Development Setup
- No external configuration files; all settings are in `poe.py` as variables.
- Script is intended to run on any system with Python 3.x and the above dependencies installed.
## Technical Constraints
- Assumes the Modbus device is accessible via TCP/IP and supports reading holding registers for temperature and humidity.
- MQTT broker must be reachable from the host running the script.
- No persistent storage or database integration; data is transient.
- No web UI or REST API; all interaction is via logs and MQTT.

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
pymodbus>=3.5.0
paho-mqtt>=1.6.0

151
sensor_bridge.py Normal file
View File

@ -0,0 +1,151 @@
import logging
import json
import time
from datetime import datetime
from pymodbus.client import ModbusTcpClient
from pymodbus.exceptions import ModbusException
# Add MQTT client
import paho.mqtt.client as mqtt
# Import configuration
from config import (
MODBUS_HOST, MODBUS_PORT, UNIT_ID,
MQTT_BROKER, MQTT_PORT, MQTT_TOPIC, MQTT_CLIENT_ID,
MQTT_USERNAME, MQTT_PASSWORD, LOCATION, PUBLISH_INTERVAL
)
# Setting logging basic to see output
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# MQTT callbacks
def on_connect(client, userdata, flags, rc):
if rc == 0:
logging.info("Connected to MQTT Broker!")
else:
logging.error(f"Cannot connect to MQTT Broker. Return code: {rc}")
def on_publish(client, userdata, mid):
logging.info(f"Successfully sent message with ID: {mid}")
def read_and_publish_data(mqtt_client, modbus_client):
"""Read data from Modbus and publish to MQTT"""
try:
# Check connection to Modbus server
if not modbus_client.is_socket_open():
if not modbus_client.connect():
logging.error("Cannot connect to Modbus server.")
return False
# Read temperature (register 0)
result_temp = modbus_client.read_holding_registers(address=0, count=1, slave=UNIT_ID)
# Read humidity (register 1)
result_hum = modbus_client.read_holding_registers(address=1, count=1, slave=UNIT_ID)
# Initialize data to publish
data = {
"time": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S"),
"location": LOCATION
}
# Process temperature
if hasattr(result_temp, 'registers') and result_temp.registers:
raw_temp = result_temp.registers[0]
raw_temperature = raw_temp * 0.1
temperature = raw_temperature - 40
logging.info(f"Raw temperature: {raw_temperature:.1f}°C (raw: {raw_temp})")
logging.info(f"Corrected temperature: {temperature:.1f}°C")
data["temperature"] = round(temperature, 1)
else:
logging.error(f"Error reading temperature: {result_temp}")
return False
# Xử lý độ ẩm
if hasattr(result_hum, 'registers') and result_hum.registers:
raw_hum = result_hum.registers[0]
humidity = raw_hum * 0.1
logging.info(f"Humidity: {humidity:.1f}%RH (raw: {raw_hum})")
data["humidity"] = round(humidity, 1)
else:
logging.error(f"Error reading humidity: {result_hum}")
return False
# Convert data to JSON with a better format
# indent=2 creates whitespace and newlines for JSON
payload = json.dumps(data, indent=2)
logging.info(f"Publishing data: {payload}")
result = mqtt_client.publish(MQTT_TOPIC, payload)
# Ensure data is sent
result.wait_for_publish()
if result.is_published():
logging.info(f"Successfully published data to topic '{MQTT_TOPIC}'")
return True
else:
logging.error("Cannot publish data")
return False
except Exception as e:
logging.error(f"Error in reading/writing data: {e}", exc_info=True)
return False
def main_loop():
"""Main function to connect and publish data in cycles"""
# Initialize MQTT client
mqtt_client = mqtt.Client(client_id=MQTT_CLIENT_ID)
mqtt_client.on_connect = on_connect
mqtt_client.on_publish = on_publish
# Set username and password if needed
if MQTT_USERNAME and MQTT_PASSWORD:
mqtt_client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
# Initialize Modbus TCP client
modbus_client = ModbusTcpClient(
host=MODBUS_HOST,
port=MODBUS_PORT,
timeout=30
)
try:
# Connect to MQTT broker
mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60)
mqtt_client.loop_start()
logging.info(f"Attempting to connect to Modbus TCP server: {MODBUS_HOST}:{MODBUS_PORT}...")
# Connect to Modbus server
if not modbus_client.connect():
logging.error("Cannot connect to Modbus server initially.")
return
logging.info("Successfully connected to Modbus server.")
logging.info(f"Starting data reading cycle every {PUBLISH_INTERVAL} seconds...")
try:
# Main loop to read and publish data
while True:
success = read_and_publish_data(mqtt_client, modbus_client)
if not success:
logging.warning("Error occurred in current cycle, will retry in next cycle.")
# Wait for next publish cycle
logging.info(f"Waiting {PUBLISH_INTERVAL} seconds until next publish...")
time.sleep(PUBLISH_INTERVAL)
except KeyboardInterrupt:
logging.info("Received stop signal from user.")
except Exception as e:
logging.error(f"Unexpected error in main loop: {e}", exc_info=True)
finally:
# Close Modbus connection
modbus_client.close()
logging.info("Successfully closed Modbus connection.")
mqtt_client.loop_stop()
mqtt_client.disconnect()
logging.info("Successfully closed MQTT connection.")