I'm trying to read arrow keys character from the terminal and use the to send String
type of messages through ROS
. For doing so I'm using termios
read()
function.
Firstly, I have done a small test that works fine under C
, compiled using gcc
. This is the code:
#include <unistd.h>
#include <stdlib.h>
#include <termios.h> // for keyboard input
#include <string.h>
#include <stdio.h>
bool quit_requested = false;
void processKeyboardInput (char c)
{
switch (c)
{
case 68:// Left arrow pressed
{
puts("Left pressed");
break;
}
case 67://Right arrow pressed
{
puts("Right pressed");
break;
}
case 65://Up arrow pressed
{
puts("Up pressed");
break;
}
case 66://Down arrow pressed
{
puts("Down pressed");
break;
}
case 'q'://Quit arrow pressed
{
quit_requested = true;
puts("q pressed, quitting...");
}
default:
{
break;
}
}
}
int main(int argc, char const *argv[]) {
int key_file_descriptor;
struct termios raw;
struct termios original_terminal_state;
tcgetattr(key_file_descriptor, &original_terminal_state); // get terminal properties
memcpy(&raw, &original_terminal_state, sizeof(struct termios));
raw.c_lflag &= ~(ICANON | ECHO);//local modes, enable canonical mode and echo
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;//special characters
raw.c_cc[VEOF] = 2;
tcsetattr(key_file_descriptor, TCSANOW, &raw);
puts("Reading from keyboard");
puts("---------------------------");
puts("Press the arrow keys to move and q to quit");
char c;
while (!quit_requested)
{
if (read(key_file_descriptor, &c, 1) < 0)
{
perror("read char failed():");
exit(-1);
}
processKeyboardInput(c);
}
tcsetattr(key_file_descriptor, TCSANOW, &original_terminal_state);
puts("Exit");
return 0;
}
Once done this, I have added ROS
stuff to send the keys using a String
message:
#include <unistd.h>
#include <termios.h> // for keyboard input
#include <cstring>
#include <stdio.h>
#include "ros/ros.h"
#include "std_msgs/String.h"
bool quit_requested = false;
std::string processKeyboardInput (char c)
{
switch (c)
{
case 68:// Left arrow pressed
{
puts("Left pressed");
return ("left");
}
case 67://Right arrow pressed
{
puts("Right pressed");
return ("right");
}
case 65://Up arrow pressed
{
puts("Up pressed");
return ("forward");
}
case 66://Down arrow pressed
{
puts("Down pressed");
return ("backward");
}
case 'q'://Quit arrow pressed
{
quit_requested = true;
puts("q pressed, quitting...");
}
default:
{
return ("");
}
}
}
int main(int argc, char **argv) {
std::string cmd;
int key_file_descriptor;
char c;
int error;
struct termios raw;
struct termios original_terminal_state;
//std::string name = "key_teleop";
/**** INIT ROS STUFFS *****/
ros::init(argc, argv, "key_teleop");
std_msgs::String msg;
ros::NodeHandle n;
ros::Publisher key_pub = n.advertise<std_msgs::String>("key_teleop", 1000);
ros::Rate loop_rate(1);
/***************************/
tcgetattr(key_file_descriptor, &original_terminal_state); // get terminal properties
memcpy(&raw, &original_terminal_state, sizeof(struct termios));
raw.c_lflag &= ~(ICANON | ECHO);//local modes, enable canonical mode and echo
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;//special characters
raw.c_cc[VEOF] = 2;
tcsetattr(key_file_descriptor, TCSANOW, &raw);
puts("Reading from keyboard");
puts("---------------------------");
puts("Press the arrow keys to move and q to quit");
while (ros::ok() & (!quit_requested))
{
//if (read(key_file_descriptor, &c, 1) < 0)
error = read(key_file_descriptor, &c, 1);
if ( error < 0)//Process the error
{//TODO I get errors, not ROSify code doesn't, C / C++ code mixture issue??
if (error == EAGAIN)
puts ("EAGAIN");
else if (error == EBADF)
puts ("EBADF");
else if (error == EFAULT)
puts ("EFAULT");
else if (error == EINTR)
puts ("EINTR");
else if (error == EINVAL)
puts ("EINVAL");
else if (error == EIO)
puts ("EIO");
else if (error = EISDIR)
puts ("EISDIR");
perror("read char failed():");
exit(-1);
}
cmd = processKeyboardInput(c);
if (!cmd.empty())
{
msg.data = cmd;
key_pub.publish(msg);
ros::spinOnce();
//loop_rate.sleep();
}
}
tcsetattr(key_file_descriptor, TCSANOW, &original_terminal_state);
puts("Exit");
return 0;
}
Once created the CMakeLists.txt
and package.xml
, I have compiled using catkin_make
. Test it out and now doesn't work, most of the times I receive a error return at read()
, it enter into EISDIR
case and prints it. Any idea why now I'm receiving this error while the previous c
example is working fine??
Thanks in advance!
Following the reccomendation of @Jonathan, I have added the flags -Wall -Werror -Wextra
to the CMake
compiling flags and discovered the next message thrown by the compiler:
error: ‘key_file_descriptor’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
I have found this nice article and changed the int key_file_descriptor
I was using for STDIN_FILENO
. So I have replaced it all over the code and now is working! I have deleted also the comparison typo. Thanks guys!