How to Create a Subscriber Node in C++
Let’s look at how to create a subscriber node in ROS.
We will write a subscriber node in C++ that will subscribe to the /message topic so that it can receive the string messages published by the publisher node (simple_publisher_node) we created in the previous section.
Open up a new terminal window.
Move to the src folder of the package: noetic_basics_part_1.
roscd noetic_basics_part_1/src
Let’s create a C++ program named simple_subscriber_node.cpp. The name for this node in ROS will be simple_subscriber_node. The purpose of this node is to subscribe to the topic named /message.
Ok, now type this command to open a brand new C++ file.
gedit simple_subscriber_node.cpp
Type the code below into the file, one line at a time so that you read all the comments and know what is going on. Creating a node in ROS that can subscribe to a topic is something you’ll do again and again as you work with ROS.
/**
 * A basic program to subscribe to messages that are published on a ROS topic
 * @author Addison Sears-Collins (https://automaticaddison.com/)
 * @version 1.0 
 */ 
 
 // Include the header file that has declarations for the standard ROS classes
 #include "ros/ros.h"
// Header file for messages of type std_msgs::String
// Each type of message you use in a program will have a C++ header file you
// need to include. The syntax is #include <package_name/type_name.h>
// Here we want to subscribe to messages of type String that are owned by the
// std_msgs package (http://wiki.ros.org/std_msgs)
#include "std_msgs/String.h"
/**
 * This function is known as a callback function. A program that subscribes to
 * to a topic (i.e. subscriber node) doesn't know when messages will be 
 * arriving. If you have code that needs to process an incoming message 
 * (e.g. most often sensor data in robotics), you place that code inside a 
 * callback function. A callback function is a function that is called once f
 * or each message that arrives.  
 * The syntax for a subscriber callback function is:
 *   void function_name(const package_name::type_name &msg) {
 *	   [insert your code here]
 *   }
 * The function below is executed each time a new message arrives.
 *   Topic Name: /message
 *   Message Type: std_msgs/String
 */
void messageCallback(const std_msgs::String::ConstPtr& msg) {
	
  // Display the message that was received to the terminal window
  ROS_INFO("The message I received was: [%s]", msg->data.c_str());
}
/**
 * Main method
 */
int main(int argc, char **argv) {
  // Initialize the node and set the name.
  // The ros::init() function needs to see argc and argv.
  // The third argument is the name of the node.
  ros::init(argc, argv, "simple_subscriber_node");	
  
  // Create the main access point for the node
  // This piece of code enables the node to communicate with the ROS system.
  ros::NodeHandle n;  
  
  // This code below is how we subscribe to a topic.
  // The syntax is:
  //   ros::Subscriber sub = node_handle.subscribe(
  //     topic_name_without_leading_slash,
  //     queue_size, callback_function);
  // I like to use a large number like 1000 for the queue size.
  // When new messages arrive on a topic, they line up in a queue until the
  // callback function is executed by ROS. If the queue is full when a new 
  // message arrives, the oldest unprocessed messages are removed to create
  // space for the new message.
  // To prevent overflow of the queue, you'll need to frequently run the 
  // ros::spinOnce or ros::spin function to make sure the callback 
  // function is executed.  
  ros::Subscriber sub = n.subscribe("message", 1000, messageCallback);
	
  // This code enters a loop, continually processing messages that arrive
  // in the queue. If you don't have this code, messages that are waiting in
  // the queue will never be processed.
  // Note, if you use ros::spinOnce() instead of ros::spin(), unprocessed 
  // messages will be processed and then the code below this line will 
  // start executing. 
  // With ros::spin(), you want ROS to wait for messages and
  // process those messages until you either hit Ctrl + C on your keyboard
  // or close down this node.
  // Here we use ros::spin() because the node's sole job is to echo (to the 
  // (terminal window) the message that it receives. However, if you have a 
  // node that needs to do something other than just printing
  // messages to a terminal screen, use 
  // ros::spinOnce(), since that gives you more control when you want the node
  // to process incoming messages.
  ros::spin();
  
  // Code was executed successfully
  return 0;
	
}
In a nutshell, what we did above was:
- Initialize the ROS system
- Subscribe to the message topic (i.e. /message)
- Spin, waiting for new messages to arrive
- When a new message arrives, call the callback function to process it (i.e. messageCallback())
How to Create a Subscriber Node in Python
Go to your package.
roscd noetic_basics_part_1
Go to the scripts folder.
cd scripts
Open a new file.
gedit simple_python_subscriber.py
Type the following code in it.
#!/usr/bin/env python3
import rospy
from std_msgs.msg import String
def callback(data):
    rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)
    
def receive_message():
    # In ROS, nodes are uniquely named. If two nodes with the same
    # name are launched, the previous one is kicked off. The
    # anonymous=True flag means that rospy will choose a unique
    # name for our 'simple_python_subscriber' node so that multiple listeners can
    # run simultaneously.
    rospy.init_node('simple_python_subscriber', anonymous=True)
    rospy.Subscriber("message_py", String, callback)
    # spin() simply keeps python from exiting until this node is stopped
    rospy.spin()
if __name__ == '__main__':
    receive_message()
Save the file and close it.
You should now have two Python files in your scripts folder of your noetic_basics_part_1 package.
 
					