Skip to content

Commit

Permalink
Merge branch 'main' into encodings
Browse files Browse the repository at this point in the history
  • Loading branch information
InvincibleRMC committed Mar 28, 2024
2 parents 7de7a58 + 5805da8 commit b0a8110
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
install/
devel/
logs/
log/
build/
bin/
lib/
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,29 @@
[![Continuous Integration](https://github.com/benjaminwp18/mqtt_ros_bridge/actions/workflows/industrial_ci_action.yml/badge.svg)](https://github.com/benjaminwp18/mqtt_ros_bridge/actions/workflows/industrial_ci_action.yml)

A ROS2 package for bridging between MQTT and ROS networks

## Installation
### Operating System
ROS2 is pretty tightly coupled to Ubuntu versions. `mqtt_ros_bridge` supports ROS2 Iron Irwini and Humble Hawksbill. Pick a [version](https://docs.ros.org/en/rolling/Releases.html) that works with your OS.

### MQTT
Install an MQTT broker like [Mosquitto](https://mosquitto.org/).

```bash
sudo apt-get update
sudo apt-get install mosquitto mosquitto-clients -y
```

Your broker should now be running:
```bash
you@ubuntu:~ $ service mosquitto status
● mosquitto.service - Mosquitto MQTT v3.1/v3.1.1 Broker
Loaded: loaded (/lib/systemd/system/mosquitto.service; enabled; vendor preset
Active: active (running) since Sat 2021-01-30 16:46:20 GMT; 1min 23s ago
Docs: man:mosquitto.conf(5)
man:mosquitto(8)
Main PID: 5390 (mosquitto)
Tasks: 1 (limit: 2063)
CGroup: /system.slice/mosquitto.service
└─5390 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
```
24 changes: 24 additions & 0 deletions launch/bridge_launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from launch.launch_description import LaunchDescription
from launch_ros.actions import Node


def generate_launch_description() -> LaunchDescription:
"""
Generate LaunchDescription for MQTT ROS bridge.
Returns
-------
LaunchDescription
Launches bridge_node.
"""
run_bridge_node = Node(
package='mqtt_ros_bridge',
executable='bridge_node',
emulate_tty=True,
output='screen'
)

return LaunchDescription([
run_bridge_node
])
107 changes: 107 additions & 0 deletions mqtt_ros_bridge/bridge_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from typing import Any
from dataclasses import dataclass

import rclpy
from rclpy.node import Node
from rclpy.publisher import Publisher
from rclpy.subscription import Subscription

from std_msgs.msg import String

import paho.mqtt.client as MQTT


@dataclass
class TopicInfo():
"""Metadata about a single topic."""

name: str
publish_on_ros: bool


TOPICS: list[TopicInfo] = [
TopicInfo('pub_topic', True),
TopicInfo('sub_topic', False)
]


class BridgeNode(Node):
"""Node to bridge MQTT and ROS."""

def __init__(self) -> None:
super().__init__('mqtt_bridge_node')

print('Creating MQTT ROS bridge node')

self.mqtt_client = MQTT.Client()
self.mqtt_client.enable_logger()
self.mqtt_client.connect('localhost', port=1883, keepalive=60)
self.mqtt_client.loop_start()

self.ros_publishers: dict[str, Publisher] = {}
self.ros_subscriptions: list[Subscription] = []

for topic_info in TOPICS:
if topic_info.publish_on_ros:
publisher = self.create_publisher(String, topic_info.name, 10)
self.ros_publishers[topic_info.name] = publisher
self.mqtt_client.subscribe(topic_info.name)
else:
subscription = self.create_subscription(
String, topic_info.name, self.make_ros_receiver(topic_info.name), 10)
self.ros_subscriptions.append(subscription)

self.mqtt_client.on_message = self.mqtt_msg_received

def make_ros_receiver(self, topic: str):
"""
Create a callback function which re-publishes messages on the same topic in MQTT.
Parameters
----------
topic : str
the topic that the callback will publish on
"""
def callback(msg: String):
self.get_logger().info(f"ROS RECEIVED: Topic: '{topic}' Payload: '{msg}'")
self.mqtt_client.publish(topic, msg.data)

return callback

def mqtt_msg_received(self, _client: MQTT.Client, _userdata: Any, mqtt_msg: MQTT.MQTTMessage):
"""
Re-publish messages from MQTT on the same topic in ROS.
Parameters
----------
_client : MQTT.Client
unused; the MQTT client which received this message
_userdata : Any
unused; the private user data as set for the client
mqtt_msg : MQTT.MQTTMessage
the message received over MQTT
"""
self.get_logger().info(
f"MQTT RECEIVED: Topic: '{mqtt_msg.topic}' Payload: '{mqtt_msg.payload!r}'")

ros_msg = String()
ros_msg.data = mqtt_msg.payload.decode('utf-8')
self.ros_publishers[mqtt_msg.topic].publish(ros_msg)


def main(args=None):
"""Run bridge node; used in ROS executable."""
rclpy.init(args=args)

bridge_node = BridgeNode()

rclpy.spin(bridge_node)

bridge_node.destroy_node()
rclpy.shutdown()


if __name__ == '__main__':
main()
2 changes: 2 additions & 0 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
<license>Apache License 2.0</license>

<depend>rclpy</depend>
<depend>python3-paho-mqtt</depend>

<exec_depend>ros2launch</exec_depend>
<exec_depend>std_msgs</exec_depend>

<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
tests_require=['pytest'],
entry_points={
'console_scripts': [
'bridge_node = mqtt_ros_bridge.bridge_node:main'
],
},
)

0 comments on commit b0a8110

Please sign in to comment.