POE Sensor - Modbus to MQTT Bridge Service
A robust Python service that reads temperature, humidity, and CO2 data from multiple Modbus TCP sensors and publishes the data to an MQTT broker with comprehensive monitoring and alerting capabilities.
📁 Project Structure
POE-sensor/
├── config.py # Configuration constants and sensor definitions
├── sensor_bridge.py # Core logic for Modbus and MQTT operations
├── sensor_tracker.py # Sensor status tracking and alerting system
├── health_check.py # HTTP health check server (optional monitoring)
├── main.py # Entry point and application lifecycle management
├── requirements.txt # Python dependencies
├── poe-sensor.nomad # Nomad deployment configuration
└── README.md # This documentation
🚀 Quick Start
1. Install Dependencies
pip install -r requirements.txt
2. Configure Settings
Edit config.py
to match your environment:
# Modbus sensor configuration
MODBUS_HOSTS = [
{"ip": "10.84.60.31", "location": "Warehouse-B", "type": "temperature_humidity"},
{"ip": "10.84.60.37", "location": "Office", "type": "cwt_co2"},
# Add more sensors as needed
]
# MQTT configuration
MQTT_BROKER = "mqtt.service.mesh"
MQTT_USERNAME = "relay"
MQTT_PASSWORD = "your_password"
3. Run the Service
python main.py
📊 Features
Core Functionality
- Multi-Sensor Support: Handles both temperature/humidity and CO2 sensors
- Modbus TCP Client: Reads data from multiple Modbus holding registers
- MQTT Publisher: Publishes sensor data to structured MQTT topics
- Data Processing: Converts raw sensor values to calibrated readings
- Error Handling: Robust error handling and retry mechanisms
- Resource Management: Automatic connection cleanup and garbage collection
Monitoring & Alerting
- Real-time Sensor Tracking: Individual sensor health monitoring
- Automatic Alerting: MQTT alerts for sensor failures and recovery
- Health Check API: Optional HTTP endpoints for system monitoring
- Comprehensive Logging: Detailed logging for debugging and monitoring
- Status Reporting: Real-time sensor status via MQTT topics
Production Ready
- Nomad Deployment: Ready-to-deploy Nomad job configuration
- Docker Support: Containerized deployment with dependency management
- Environment Configuration: Environment variable support for flexible deployment
- Graceful Shutdown: Proper cleanup and signal handling
📈 Data Format & Topics
MQTT Topic Structure
The service uses a flat topic structure for each location:
Location/{location}/Time # Timestamp
Location/{location}/Status # "online" or "offline"
Location/{location}/Temperature # Temperature value
Location/{location}/Humidity # Humidity value
Location/{location}/CO2 # CO2 sensors only
Location/{location}/alerts # Alert messages
Note: The
CO2
topic is only published for sensors that support CO2 measurement.
Example Topics
Location/Warehouse-B/Time → "2024-01-15 10:30:25"
Location/Warehouse-B/Status → "online"
Location/Warehouse-B/Temperature → "23.5"
Location/Warehouse-B/Humidity → "65.2"
Location/Office/Time → "2024-01-15 10:30:25"
Location/Office/Status → "online"
Location/Office/Temperature → "24.1"
Location/Office/Humidity → "58.3"
Location/Office/CO2 → "420"
Alert Format
{
"alert_type": "sensor_failure",
"timestamp": "2024-01-15T10:30:25Z",
"sensor_id": "10.84.60.31_Warehouse-B",
"sensor_ip": "10.84.60.31",
"sensor_location": "Warehouse-B",
"sensor_type": "temperature_humidity",
"consecutive_failures": 3,
"last_error": "Connection timeout",
"severity": "critical"
}
🔧 Configuration
Sensor Configuration
Add sensors to the MODBUS_HOSTS
list in config.py
:
MODBUS_HOSTS = [
# Temperature/Humidity sensors
{"ip": "10.84.60.31", "location": "Warehouse-B", "type": "temperature_humidity"},
{"ip": "10.84.60.32", "location": "Warehouse-C", "type": "temperature_humidity"},
# CO2 sensors (CWT series)
{"ip": "10.84.60.37", "location": "Office", "type": "cwt_co2"},
]
Environment Variables
The service supports environment variable configuration:
Variable | Description | Default |
---|---|---|
MQTT_BROKER |
MQTT broker address | "mqtt.service.mesh" |
MQTT_PORT |
MQTT broker port | 1883 |
MQTT_USERNAME |
MQTT username | "relay" |
MQTT_PASSWORD |
MQTT password | - |
PUBLISH_INTERVAL |
Data publish interval (seconds) | 10 |
HEALTH_CHECK_ENABLED |
Enable HTTP health server | true |
HEALTH_CHECK_PORT |
Health server port | 8080 |
ALERTING_ENABLED |
Enable MQTT alerts | true |
SENSOR_TIMEOUT_THRESHOLD |
Failures before alert | 3 |
RECOVERY_CONFIRMATION_COUNT |
Successes to confirm recovery | 2 |
TIMEZONE_OFFSET |
Timezone offset from UTC (hours) | 7 (GMT+7) |
🏥 Health Check & Monitoring
Optional Health Check Server
The service includes an optional HTTP health check server for monitoring:
- http://localhost:8080/health - Basic service health status
- http://localhost:8080/sensors - Detailed sensor status information
Health Response Example
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:25Z",
"service": "poe-sensor",
"version": "1.0.0",
"sensors_total": 7,
"sensors_online": 6,
"sensors_health": 85.7
}
Sensor Status Response
{
"timestamp": "2024-01-15T10:30:25Z",
"total_sensors": 7,
"online_sensors": 6,
"offline_sensors": 1,
"sensors": {
"10.84.60.31_Warehouse-B": {
"ip": "10.84.60.31",
"location": "Warehouse-B",
"type": "temperature_humidity",
"status": "online",
"last_success": "2024-01-15T10:29:45Z",
"uptime_percentage": 98.5
}
}
}
🚀 Production Deployment
Nomad Deployment
The service is designed for production deployment using HashiCorp Nomad:
# Deploy to Nomad cluster
nomad job run poe-sensor.nomad
# Check deployment status
nomad job status poe-sensor
# View logs
nomad alloc logs <allocation_id>
Key Deployment Features
- No Health Check Dependencies: Service is considered healthy when running
- Fast Deployment: Minimal startup time requirements
- Auto-restart: Automatic restart on application crashes
- Resource Limits: Configured CPU and memory limits
- Log Management: Automatic log rotation and retention
Docker Configuration
The Nomad job uses a pre-configured Docker image with:
- Python 3.x runtime
- Required system dependencies
- Automatic dependency installation
- Git-based source code deployment
📡 Sensor Support
Temperature/Humidity Sensors
- Register 0: Temperature (formula: (125 - (-40)) * raw / 1650 - 40)
- Register 1: Humidity (formula: raw * 100 / 1000)
- Output: Temperature in °C, Humidity in %RH
CWT CO2 Sensors
- Register 0: Humidity (0.1%RH resolution)
- Register 1: Temperature (0.1°C resolution, 2's complement)
- Register 2: CO2 (1ppm resolution)
- Output: Temperature in °C, Humidity in %RH, CO2 in ppm
🔧 Troubleshooting
Common Issues
-
Sensor Connection Failures
- Check network connectivity to sensor IPs
- Verify Modbus port (default: 505) is accessible
- Check sensor power and network configuration
-
MQTT Connection Issues
- Verify MQTT broker address and credentials
- Check network connectivity to MQTT broker
- Review MQTT broker logs for connection attempts
-
Health Check Not Responding
- Health check server is optional and non-blocking
- Service will continue running even if health server fails
- Check port 8080 availability if health check is needed
Log Analysis
Monitor logs for key indicators:
- Connection status: Modbus and MQTT connection messages
- Data reading: Successful sensor data retrieval
- Publishing status: MQTT message publication confirmation
- Error conditions: Connection failures and retry attempts
- Sensor health: Failure/recovery notifications
Performance Monitoring
- Cycle completion: Monitor sensor processing cycles
- Memory usage: Automatic garbage collection logs
- Alert frequency: Sensor failure and recovery alerts
- Uptime percentage: Individual sensor reliability metrics
🤝 Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Make your changes with proper testing
- Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Development Guidelines
- Follow Python PEP 8 style guidelines
- Add comprehensive logging for new features
- Test with multiple sensor types
- Update documentation for configuration changes
- Ensure backward compatibility when possible
📝 License
This project is licensed under the MIT License - see the LICENSE file for details.
🆘 Support
For issues and questions:
- Check the troubleshooting section above
- Review application logs for error details
- Verify network connectivity and sensor configuration
- Create an issue in the project repository with:
- Detailed error description
- Relevant log excerpts
- Environment configuration
- Steps to reproduce the issue