NexusBus: A robust FastAPI-based middleware for Modbus TCP/RTU, offering a database-driven gateway for seamless data integration. It provides dynamic device management via REST API, automated polling with in-memory caching, real-time MQTT publishing, and hot-reload capabilities for configurations.
| Quick Start | Device Management | Polling | MQTT | Monitoring | Migrations |
|---|---|---|---|---|---|
| Database Setup | Device API Guide | Polling Config | MQTT Guide | Metrics & Monitoring | Migration Guide |
- β Database-Driven Configuration - Store and manage Modbus devices in PostgreSQL
- β Dynamic Device Management - Add/update/remove devices via REST API without restart
- β Automatic Polling - Configure registers to poll automatically from database
- β Parallel Polling - Poll multiple devices concurrently for improved performance
- β Hot-Reload - Apply configuration changes without server restart
- β Connection Pooling - Efficiently manage Modbus TCP connections & shared gateways
- β Circuit Breaker - Automatic failure detection with fast-fail and auto-recovery
- β Request Timeout Handling - Automatic timeout and connection reset
- β REST API - Complete API for device interaction and management
- β Async Support - Full async/await with asyncpg for optimal performance
- β Smart Caching - Register value caching with TTL and automatic eviction
- β MQTT Integration - Real-time data publishing to MQTT brokers
- β Soft Delete - Deactivate devices/polling without losing configuration
- β Structured Logging - JSON-formatted logs for easy parsing and monitoring
- β Metrics Collection - Built-in metrics for performance monitoring
- β Enhanced Health Checks - Comprehensive health monitoring for all services
- β Input Validation - Robust validation for all API inputs
# Clone repository
git clone https://github.com/Jonathan0823/nexusbus.git
cd nexusbus
# Create virtual environment
python -m venv venv
.\venv\Scripts\Activate.ps1 # Windows PowerShell
# source venv/bin/activate # Linux/Mac
# Install dependencies
pip install -r requirements.txtConfigure Environment:
cp .env.example .env
# Edit .env with your PostgreSQL credentialsRun Migrations:
python migrate.pyThis creates:
- β
modbus_devicestable with sample devices - β
polling_targetstable with sample polling configs
uvicorn main:app --reloadAccess:
- API: http://localhost:8000
- Interactive Docs: http://localhost:8000/docs
- Health Check: http://localhost:8000/health
- Metrics: http://localhost:8000/api/metrics
For easier setup and deployment, you can use Docker Compose to run the application and its PostgreSQL database.
docker-compose up -dThis command will:
- Build the
nexusbus_appDocker image and start the container. - Create and start a PostgreSQL database container (
nexusbus_db). - Automatically run database migrations.
- Expose the application on port
8000.
Access:
- API:
http://localhost:8000 - Interactive Docs:
http://localhost:8000/docs - Health Check:
http://localhost:8000/health
To stop the services:
docker-compose down| Endpoint | Method | Description | Docs |
|---|---|---|---|
/api/devices |
GET | List devices | β |
/api/devices/{id}/registers |
GET | Read registers | β |
/api/devices/{id}/registers/write |
POST | Write register | β |
/api/devices/gateways |
GET | Gateway status | β |
| Endpoint | Method | Description | Docs |
|---|---|---|---|
/api/admin/devices |
GET | List all devices | β |
/api/admin/devices |
POST | Create device | β |
/api/admin/devices/{id} |
PUT | Update device | β |
/api/admin/devices/{id} |
DELETE | Soft delete | β |
/api/admin/devices/reload |
POST | Hot-reload configs | β |
| Endpoint | Method | Description | Docs |
|---|---|---|---|
/api/admin/polling |
GET | List polling targets | β |
/api/admin/polling |
POST | Create polling target | β |
/api/admin/polling/{id} |
PUT | Update target | β |
/api/admin/polling/{id} |
DELETE | Soft delete | β |
| Endpoint | Method | Description | Docs |
|---|---|---|---|
/api/admin/cache |
GET | Inspect all cache | - |
/api/admin/cache/stats |
GET | Cache statistics | - |
/api/admin/cache/device/{id} |
GET | Inspect device cache | - |
/api/admin/cache |
DELETE | Clear all cache | - |
| Endpoint | Method | Description | Docs |
|---|---|---|---|
/api/metrics |
GET | Get all application metrics | - |
/api/metrics/reset |
POST | Reset metrics (testing) | - |
/health |
GET | Comprehensive health check | - |
Complete API Documentation β
This application uses a .env file for configuration. Copy .env.example to .env to start.
Database Configuration
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string. | postgresql+asyncpg://postgres:postgres@localhost:5432/modbus_db |
DATABASE_ECHO |
If true, logs SQL queries. |
false |
MQTT Configuration (Optional)
| Variable | Description | Default |
|---|---|---|
MQTT_BROKER_HOST |
Broker hostname/IP. Set to enable MQTT. | None |
MQTT_BROKER_PORT |
Broker port number. | 1883 |
MQTT_USERNAME |
MQTT Username. | None |
MQTT_PASSWORD |
MQTT Password. | None |
MQTT_TOPIC_PREFIX |
Prefix for published topics. | modbus/data |
Application Settings
| Variable | Description | Default |
|---|---|---|
APP_NAME |
Application name. | NexusBus |
APP_VERSION |
Application version. | 0.1.0 |
POLL_INTERVAL_SECONDS |
Polling interval for background service. | 5 |
CACHE_TTL_SECONDS |
Cache entry time-to-live in seconds. | 300 |
Logging Configuration
| Variable | Description | Default |
|---|---|---|
LOG_LEVEL |
Logging level (DEBUG, INFO, WARNING, ERROR). | INFO |
LOG_JSON |
Output logs in JSON format (for production). | false |
LOG_INCLUDE_CALLER |
Include caller information in logs. | true |
Circuit Breaker Configuration
| Variable | Description | Default |
|---|---|---|
CIRCUIT_BREAKER_FAILURE_THRESHOLD |
Consecutive failures before opening. | 5 |
CIRCUIT_BREAKER_RECOVERY_TIMEOUT |
Seconds before attempting recovery. | 30 |
| Parameter | Type | Required | Description | Default |
|---|---|---|---|---|
device_id |
string | β | Unique device identifier | - |
host |
string | β | IP address or hostname | - |
port |
integer | β | TCP port (e.g., 502, 8899) | - |
slave_id |
integer | β | Modbus slave ID (1-247) | - |
timeout |
integer | β | Connection timeout (seconds) | 10 |
framer |
string | β | RTU, SOCKET, or ASCII | RTU |
max_retries |
integer | β | Max retry attempts (0-10) | 5 |
retry_delay |
float | β | Delay between retries (seconds) | 0.1 |
Note: All inputs are validated automatically:
slave_id: Must be between 1-247 (Modbus specification)port: Must be between 1-65535framer: Must be one of RTU, SOCKET, or ASCIItimeout: Must be between 1-300 secondsmax_retries: Must be between 0-10
# Read 10 holding registers starting at address 0
curl "http://localhost:8000/api/devices/office-eng/registers?address=0&count=10"
# Read from cache (faster, uses polling data)
curl "http://localhost:8000/api/devices/office-eng/registers?address=0&count=10&source=cache"curl -X POST http://localhost:8000/api/devices/office-eng/registers/write \
-H "Content-Type: application/json" \
-d '{"address": 10, "value": 123, "register_type": "holding"}'curl -X POST http://localhost:8000/api/admin/devices \
-H "Content-Type: application/json" \
-d '{
"device_id": "warehouse-1",
"host": "192.168.1.100",
"port": 502,
"slave_id": 3,
"framer": "SOCKET"
}'
# Reload to apply changes
curl -X POST http://localhost:8000/api/admin/devices/reload# Create polling target
curl -X POST http://localhost:8000/api/admin/polling \
-H "Content-Type: application/json" \
-d '{
"device_id": "office-eng",
"register_type": "holding",
"address": 0,
"count": 10,
"description": "Main control registers"
}'
# Changes apply automatically (hot-reload)
# Wait 5 seconds, then check cache
curl "http://localhost:8000/api/devices/office-eng/registers?address=0&count=10&source=cache"modbus_middleware/
βββ .env.example # Environment variables template
βββ DATABASE_SETUP.md # Setup guide & docs index
βββ main.py # Application entry point
βββ migrate.py # Main migration runner
βββ requirements.txt # Python dependencies
β
βββ app/
β βββ __init__.py
β βββ dependencies.py # Dependency injection
β βββ schemas.py # Pydantic schemas
β βββ api/
β β βββ __init__.py
β β βββ admin_routes.py # Admin device management
β β βββ cache_routes.py # Admin cache inspection
β β βββ metrics_routes.py # Metrics endpoint
β β βββ polling_routes.py # Admin polling management
β β βββ routes.py # Device API endpoints
β βββ config/
β β βββ __init__.py
β β βββ devices.py # Device configuration loader
β βββ core/
β β βββ __init__.py
β β βββ cache.py # Register caching with TTL
β β βββ config.py # Application settings
β β βββ logging_config.py # Structured logging configuration
β β βββ metrics.py # Metrics collection
β β βββ modbus_client.py # Modbus client manager
β β βββ mqtt_client.py # MQTT client manager
β βββ database/
β β βββ __init__.py
β β βββ connection.py # Database connection
β β βββ crud.py # CRUD operations
β β βββ models.py # SQLModel database models
β βββ services/
β βββ __init__.py
β βββ poller.py # Background polling service
β
βββ docs/ # Documentation
β βββ CACHE_INSPECTION_GUIDE.md # Cache debugging guide
β βββ DEVICE_MANAGEMENT.md # Device API guide
β βββ METRICS_AND_MONITORING.md # Metrics and monitoring guide
β βββ MQTT_INTEGRATION.md # MQTT integration guide
β βββ POLLING_CONFIGURATION.md # Polling guide
β βββ POLLING_QUICK_START.md # Quick polling guide
β
βββ migrations/ # Database migrations
βββ README.md # Migration guide
βββ base.py # Migration utilities
βββ 001_initial_setup.py # Migration: Create devices table
βββ 002_add_polling_targets.py # Migration: Create polling table
No restart needed!
# 1. Add device
curl -X POST /api/admin/devices -d '{...}'
# 2. Reload (applies instantly)
curl -X POST /api/admin/devices/reload
# 3. Use immediately
curl "http://localhost:8000/api/devices/new-device/registers?..."Polling auto-reloads every cycle (default: 5 seconds).
Configure registers to poll automatically and serve from cache:
# Configure polling via database
POST /api/admin/polling
# Data polled every 5 seconds
# Access cached data (fast!)
GET /api/devices/{id}/registers?source=cache- Shared Gateways: Multiple devices on same gateway share one connection
- Circuit Breaker: After 5 failures, requests fast-fail with 503 for 30s, then auto-retry
- Auto Recovery: Timeout handling with automatic reconnection
- Request Serialization: Prevents slave ID conflicts
- Thread Pooling: Non-blocking Modbus operations
# 1. Check health
curl http://localhost:8000/health
# 2. List devices
curl http://localhost:8000/api/admin/devices
# 3. Test read
curl "http://localhost:8000/api/devices/office-eng/registers?address=0&count=10"
# 4. Check gateway status
curl http://localhost:8000/api/devices/gatewaysuvicorn main:app --reload --log-level debug# Create migration file
cp migrations/001_initial_setup.py migrations/003_my_feature.py
# Edit the migration
# Update migrate.py to include it
# Run it
python migrate.py --migration 003# β WRONG - changes won't apply
curl -X POST /api/admin/devices -d '{...}'
# β
CORRECT - reload to apply
curl -X POST /api/admin/devices -d '{...}'
curl -X POST /api/admin/devices/reloadPolling configuration reloads automatically every polling cycle. No manual reload needed for polling!
- DELETE endpoints set
is_active=false(preserves data) - Reactivate with
/activateendpoint - Data remains in database for audit trail
- Getting Started: DATABASE_SETUP.md
- Device Management: DEVICE_MANAGEMENT.md
- Polling Setup: POLLING_CONFIGURATION.md
- Quick Testing: POLLING_QUICK_START.md
- Metrics & Monitoring: METRICS_AND_MONITORING.md
- Database Migrations: migrations/README.md
| I want to... | Read this |
|---|---|
| Set up the database | DATABASE_SETUP.md |
| Add a new device | Device Creation Guide |
| Configure polling | Polling Configuration |
| Monitor performance | Metrics & Monitoring |
| Set up logging | Metrics & Monitoring |
| Create a migration | Migration Guide |
| Troubleshoot issues | Troubleshooting |
Check .env configuration:
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/modbus_dbIf database is unavailable, app falls back to hardcoded configs in app/config/devices.py.
Did you forget to reload?
curl -X POST http://localhost:8000/api/admin/devices/reload- Check device connectivity:
ping {host} - Verify port is open
- Confirm slave_id is correct
- Check gateway status:
GET /api/devices/gateways - Increase timeout via Admin API
Apache License 2.0 (Copyright (c) 2025 Eguin Jonathan)
- π Complete Documentation Index
- π§ Device API Reference
- π Polling Configuration
- π‘ MQTT Integration
- π Metrics & Monitoring
- ποΈ Database Migrations
- π» Interactive API Docs (when running)
Built with β€οΈ using FastAPI, PostgreSQL, SQLModel, pymodbus, and MQTT