Event-driven MQTT bridge for real-world reinforcement learning
Project description
mqtt-rl-bridge — Event-Driven MQTT Bridge for Real-World Reinforcement Learning
A lightweight, framework-agnostic package that connects any MQTT-enabled sensor or robot to a reinforcement learning (RL) agent — in real time.
Use it to train RL policies on physical systems:
- HVAC controllers
- Robotic arms
- Drones
- Smart agriculture
- Industrial IoT
- Home automation
No Gymnasium dependency. No hardcoded examples. Just three abstract methods to implement.
Features
- Zero RL framework lock-in — works with Stable-Baselines3, RLlib, CleanRL, or your custom loop
- Event-driven MQTT — real-time sensor updates via
paho-mqtt - Thread-safe message queue — no race conditions
- Timeout fallbacks — robust to network glitches
- Minimal API — subclass
MQTTRLEnvand implement 3 methods - No hardware in tests — full unit-testable with mocks
Installation
pip install mqtt-rl-bridge
Optional extras:
pip install "mqtt-rl-bridge[gym]" # for Gymnasium space support
pip install "mqtt-rl-bridge[dev]" # for testing + linting
Quick Start
1. Create Your Environment
# my_env.py
import numpy as np
from mqtt_rl_bridge import MQTTRLEnv
class TemperatureControlEnv(MQTTRLEnv):
def __init__(self, **kwargs):
super().__init__(
sensor_topic="home/sensors",
action_topic="home/hvac",
timeout=8.0,
step_delay=2.0, # wait for HVAC to respond
**kwargs
)
def _extract_observation(self, raw: dict) -> np.ndarray:
temp = float(raw.get("temperature", 22.0))
target = float(raw.get("setpoint", 22.0))
return np.array([temp, target], dtype=np.float32)
def _encode_action(self, action: int) -> dict:
modes = ["cool", "off", "heat"]
return {"mode": modes[action]}
def _compute_reward(self, obs, action, next_obs) -> float:
temp, target = next_obs
error = abs(temp - target)
energy = 0.1 if action != 1 else 0.0
return -error - energy
def _is_done(self, obs, action) -> tuple[bool, bool]:
temp, target = obs
return abs(temp - target) < 0.5, False
2. Use in Any RL Loop
Custom Training Loop
from my_env import TemperatureControlEnv
import numpy as np
env = TemperatureControlEnv(broker_host="192.168.1.100")
obs = env.reset()
for _ in range(200):
action = np.random.randint(0, 3)
obs, reward, done, truncated, info = env.step(action)
print(f"Temp: {obs[0]:.1f}°C → Reward: {reward:.3f}")
if done or truncated:
obs = env.reset()
env.close()
Stable-Baselines3 (PPO)
from my_env import TemperatureControlEnv
from stable_baselines3 import PPO
env = TemperatureControlEnv(broker_host="192.168.1.100")
model = PPO("MlpPolicy", env, verbose=1, learning_rate=3e-4)
model.learn(total_timesteps=10_000)
model.save("temp_control_ppo")
env.close()
Ray RLlib
import ray
from ray.rllib.algorithms.ppo import PPOConfig
from my_env import TemperatureControlEnv
ray.init()
config = PPOConfig().environment(
TemperatureControlEnv,
env_config={"broker_host": "192.168.1.100"}
)
algo = config.build()
algo.train()
algo.save("rllib_checkpoint")
algo.stop()
API Reference
MQTTRLEnv — Abstract Base Class
class MQTTRLEnv(ABC):
def __init__(
self,
broker_host: str = "127.0.0.1",
sensor_topic: str = "sensors",
action_topic: str = "actions",
timeout: float = 5.0,
step_delay: float = 0.0,
)
| Parameter | Description |
|---|---|
broker_host |
MQTT broker IP or hostname |
sensor_topic |
Topic where sensor data is published |
action_topic |
Topic where agent sends actions |
timeout |
Max wait time for sensor message (seconds) |
step_delay |
Sleep after sending action (for hardware latency) |
Methods You Must Implement
| Method | Signature | Purpose |
|---|---|---|
_extract_observation |
raw: dict → np.ndarray |
Parse JSON payload into observation |
_encode_action |
action: Any → dict |
Convert action to MQTT JSON |
_compute_reward |
obs, action, next_obs → float |
Reward function |
Optional Overrides
| Method | Default | Use Case |
|---|---|---|
_is_done |
(False, False) |
Terminate episode |
reset_hook |
pass |
Send reset command on reset() |
Core Methods (Gymnasium-Compatible)
obs = env.reset() → np.ndarray
obs, reward, terminated, truncated, info = env.step(action)
env.close()
Works with any RL library that expects this signature.
Testing Without Hardware
from unittest.mock import MagicMock
from mqtt_rl_bridge import MQTTRLEnv
class MockEnv(MQTTRLEnv):
def __init__(self):
super().__init__(broker_host="localhost")
self.broker.get_message = MagicMock(
return_value={"temp": 25.0, "humidity": 60.0}
)
def _extract_observation(self, raw): return np.array([raw["temp"]])
def _encode_action(self, a): return {"fan": "on" if a == 1 else "off"}
def _compute_reward(self, o, a, no): return -abs(no[0] - 22.0)
Run unit tests without a broker.
Project Structure
my-iot-rl-project/
├── envs/
│ └── temperature_env.py
├── models/
├── train_ppo.py
├── requirements.txt
└── tests/
requirements.txt:
mqtt-rl-bridge
stable-baselines3
gymnasium
Contributing
- Fork the repo
- Create a branch:
git checkout -b feature/my-cool-env - Install dev deps:
pip install "mqtt-rl-bridge[dev]" - Write tests in
tests/ - Run:
pytest - Submit PR
Citation
@software{mqtt-rl-bridge-2025,
author = {Alex Kagozi},
title = {mqtt-rl-bridge: Event-Driven MQTT Bridge for Real-World RL},
year = {2025},
publisher = {GitHub},
url = {https://github.com/kagozi/mqtt_rl_bridge}
}
License
MIT License — free for commercial and research use.
Made with Real-World RL in Mind
No toy environments. No simulation gaps.
Train your agent directly on the physical world.
Ready to connect your robot?
Just pip install mqtt-rl-bridge and subclass MQTTRLEnv.
Made with love for real hardware and real learning.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mqtt_rl_bridge-0.1.0.tar.gz.
File metadata
- Download URL: mqtt_rl_bridge-0.1.0.tar.gz
- Upload date:
- Size: 7.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d4199e3db2dfbd3a47c9a0704565bda5c11a434d7aed6ab5416ed2cdf2588632
|
|
| MD5 |
b2224108f6c93e667c72b764a024bdef
|
|
| BLAKE2b-256 |
bbcf89c964f4cc5d778e787055ac6838a1c67f33e46f89bcd05cf2194f3bafa0
|
File details
Details for the file mqtt_rl_bridge-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mqtt_rl_bridge-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fc97ef98675d3d86184faf3919c5fac12420068d679b40a24db98124c5aee04
|
|
| MD5 |
c86d42a468771fb71252b3d5511efd4c
|
|
| BLAKE2b-256 |
2bdd1ace58e89bb9cadfdc98108a4f8aaf881a1ef40bf9572e9bddd41b4fac12
|