ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
1

rosjava publishing to a topic based on user input (button)

asked 2017-10-13 08:41:48 -0500

agkhalil gravatar image

I have been playing around with rosjava now for a few days and it is great. I am able to connect my android phone to ros master over usb/wifi and have them publish/subscribe to one another.

What I am trying to do now is add a button on my android app, when the user hits it, the message being published changes. To clarify: I have a publisher node "talkernode" created that starts by publishing a String "Hello" message on a "/chatter" topic. When the user hits a button, I want the String message to change to something of my choice.

Below is the relevant portion of my code. The publishGo() method is my onClick callback function, where I am trying to add code to change the message being published. I have already tested that something happens when the button is hit and it works.

public class MainActivity extends RosActivity{
Button btn;

public MainActivity() {
    super("Example", "Example");
}

@Override
public void startMasterChooser() {
    URI uri;
    try {
        uri = new URI("http://192.168.42.19:11311/");
    } catch (URISyntaxException e) {
        throw new RosRuntimeException(e);
    }

    nodeMainExecutorService.setMasterUri(uri);
    new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            MainActivity.this.init(nodeMainExecutorService);
            return null;
        }
    }.execute();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    btn = (Button) findViewById(R.id.robot_button);
}

@Override
protected void init(NodeMainExecutor nodeMainExecutor) {
    Talker talkernode = new Talker(this);

    NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic(
            InetAddressFactory.newNonLoopback().getHostAddress());
    nodeConfiguration.setMasterUri(getMasterUri());

    nodeMainExecutor.execute(talkernode, nodeConfiguration);
}

protected void publishGo(View view) {
    // Code here
}
}

Below is the Talker class code. randomCommand is the string being published and the string I want to change when the button is clicked.

class Talker extends AbstractNodeMain {
private String randomCommand = "Hello";
Talker(MainActivity mainActivity) {

}

@Override
public GraphName getDefaultNodeName() {
    return GraphName.of("rosjava_tutorial_pubsub/talker");
}

@Override
public void onStart(final ConnectedNode connectedNode) {
    final Log log = connectedNode.getLog();
    final Publisher<std_msgs.String> publisher =
            connectedNode.newPublisher("chatter", std_msgs.String._TYPE);
    // This CancellableLoop will be canceled automatically when the node shuts
    // down.
    connectedNode.executeCancellableLoop(new CancellableLoop() {
        @Override
        protected void setup() {
        }

        @Override
        protected void loop() throws InterruptedException {
            std_msgs.String str = publisher.newMessage();
            str.setData(randomCommand);
            log.info("I publish: \"" + randomCommand + "\"");
            publisher.publish(str);
            Thread.sleep(1000);
        }
    });

}
}

I apologize if this is an easy question or if it has been answered before, but any help would be immensely appreciated.

edit retag flag offensive close merge delete

1 Answer

Sort by ยป oldest newest most voted
1

answered 2017-10-13 15:05:48 -0500

jubeira gravatar image

updated 2017-10-13 15:06:36 -0500

@agkhalil what you need in this case is a publisher "on demand".

First of all, save the publisher object in a member variable in the node in the onStart method, so that you can use it later. You have to do this in onStart because the ConnectedNode provides you the publisher.

class Talker extends AbstractNodeMain {
Publisher<std_msgs.String> publisher;
(...)

@Override
public void onStart(final ConnectedNode connectedNode) {
    publisher = connectedNode.newPublisher("chatter", std_msgs.String._TYPE);
    (...)
}
}

Second, add a publish method to your node that you can call whenever you want:

// Note that you shouldn't call this before onStart is called; 
// check that publisher is not null before using it!
public void publish(String message) {
    std_msgs.String toPublish = publisher.newMessage();
    toPublish.setData(message);
    publisher.publish(toPublish);
}

Then, just store your Talker node somewhere you can access from the callback button, and call publish from there. That should cover your use case. Remember that there is a period of time between the moment you create the node and the moment onStart is called; that's why you should be careful with the state of publisher member variable.

Bonus: Note that you can also make your publisher latched; if you do so, the publisher will publish the message to each new subscriber and you won't need to do anything else. To do so, after creating the publisher, do publisher.setLatchMode(true).

Hope it helps!

edit flag offensive delete link more

Comments

Thanks a bunch man! It works!

agkhalil gravatar image agkhalil  ( 2017-10-13 19:48:27 -0500 )edit

Question Tools

2 followers

Stats

Asked: 2017-10-13 08:41:48 -0500

Seen: 1,073 times

Last updated: Oct 13 '17