Go to the documentation of this file.00001
00026 #include "SDL.h"
00027 #include <string>
00028
00029 #include "diagnostic_updater/diagnostic_updater.h"
00030 #include "ros/ros.h"
00031 #include "sensor_msgs/Joy.h"
00032
00033 #include "joystick_sdl/joystick.h"
00034
00035
00036 namespace joystick_sdl
00037 {
00038
00044 struct Joystick::Impl
00045 {
00046 void timerCallback(const ros::TimerEvent&);
00047 bool attemptConnection();
00048 void diagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat);
00049 void addMappingsFromFile(std::string filename);
00050 double scaleAxis(int32_t unscaled_value);
00051 const char* getPowerLevel(SDL_Joystick* joy_handle);
00052
00053 ros::Time last_connection_attempt_time;
00054 ros::Duration connection_attempt_period;
00055 ros::Publisher joy_pub;
00056 ros::Timer poll_timer;
00057 sensor_msgs::Joy joy_msg;
00058 diagnostic_updater::Updater diag_updater;
00059
00060 double deadzone;
00061
00062 int num_joysticks;
00063 SDL_Joystick* joy_handle;
00064 int num_axes;
00065 int num_buttons;
00066
00067 Impl();
00068 ~Impl();
00069 };
00070
00071 Joystick::Joystick(ros::NodeHandle* nh, ros::NodeHandle* nh_param)
00072 {
00073 pimpl_ = new Impl;
00074
00075
00076
00077
00078
00079
00080 nh_param->param<double>("deadzone", pimpl_->deadzone, 0.05);
00081
00082 pimpl_->joy_pub = nh->advertise<sensor_msgs::Joy>("joy", 1, true);
00083
00084 int poll_rate;
00085 nh_param->param<int>("poll_rate", poll_rate, 25);
00086 ros::Duration poll_period(1.0 / poll_rate);
00087 pimpl_->poll_timer = nh->createTimer(poll_period, &Joystick::Impl::timerCallback, pimpl_);
00088 pimpl_->poll_timer.start();
00089 }
00090
00091 Joystick::Impl::Impl() :
00092 joy_handle(NULL),
00093 connection_attempt_period(1.0),
00094 num_joysticks(0),
00095 num_axes(0),
00096 num_buttons(0)
00097 {
00098 if (SDL_Init(0))
00099 {
00100 ROS_FATAL("SDL initialization failed. Joystick will not be available.");
00101 return;
00102 }
00103 if (SDL_JoystickEventState(SDL_DISABLE) != 0)
00104 {
00105 ROS_FATAL("Setting SDL Joystick event state failed. Joystick will not be available.");
00106 return;
00107 }
00108
00109 diag_updater.add("Connection status", this, &Joystick::Impl::diagnostics);
00110 }
00111
00112 void Joystick::Impl::addMappingsFromFile(std::string filename)
00113 {
00114 int mappings_count = SDL_GameControllerAddMappingsFromFile(filename.c_str());
00115 if (mappings_count > 0)
00116 {
00117 ROS_INFO_STREAM("Added " << mappings_count << " joystick mappings from file: " << filename);
00118 }
00119 else
00120 {
00121 ROS_ERROR_STREAM("Unable to add joystick mappings from file: " << filename);
00122 }
00123 }
00124
00125 Joystick::Impl::~Impl()
00126 {
00127 if (joy_handle)
00128 {
00129 SDL_JoystickClose(joy_handle);
00130 }
00131
00132 SDL_Quit();
00133 }
00134
00135 void Joystick::Impl::timerCallback(const ros::TimerEvent&)
00136 {
00137 diag_updater.update();
00138
00139 if (!joy_handle)
00140 {
00141
00142 if (ros::Time::now() - last_connection_attempt_time > connection_attempt_period)
00143 {
00144 last_connection_attempt_time = ros::Time::now();
00145 attemptConnection();
00146 }
00147 return;
00148 }
00149
00150 SDL_JoystickUpdate();
00151 joy_msg.header.stamp = ros::Time::now();
00152
00153 if (SDL_JoystickGetAttached(joy_handle))
00154 {
00155
00156 for (int axis = 0; axis < num_axes; axis++)
00157 {
00158 joy_msg.axes[axis] = scaleAxis(SDL_JoystickGetAxis(joy_handle, axis));
00159 }
00160
00161 for (int button = 0; button < num_buttons; button++)
00162 {
00163 joy_msg.buttons[button] = SDL_JoystickGetButton(joy_handle, button);
00164 }
00165 }
00166 else
00167 {
00168
00169
00170 for (int button = 0; button < num_buttons; button++)
00171 {
00172 joy_msg.buttons[button] = 0;
00173 }
00174
00175 diag_updater.setHardwareID("");
00176 num_joysticks = 0;
00177 num_axes = 0;
00178 num_buttons = 0;
00179 SDL_JoystickClose(joy_handle);
00180 joy_handle = NULL;
00181 ROS_ERROR("Joystick disconnected, now attempting reconnection.");
00182 }
00183
00184 joy_pub.publish(joy_msg);
00185 }
00186
00187 bool Joystick::Impl::attemptConnection()
00188 {
00189
00190
00191
00192 if (SDL_WasInit(SDL_INIT_JOYSTICK) != 0)
00193 {
00194 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
00195 }
00196
00197 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0)
00198 {
00199 ROS_ERROR("Unable to initialize SDL joystick subsystem. Will try again in 1 second.");
00200 return false;
00201 }
00202
00203 num_joysticks = SDL_NumJoysticks();
00204 if (num_joysticks == 0)
00205 {
00206 ROS_ERROR("No joystick found. Will look again in 1 second.");
00207 return false;
00208 }
00209
00210 joy_handle = SDL_JoystickOpen(0);
00211 if (!joy_handle)
00212 {
00213 ROS_ERROR("Failed to connect to joystick. Will try again in 1 second.");
00214 return false;
00215 }
00216
00217 ROS_INFO("Found %d joystick(s):", num_joysticks);
00218 for (int joy_index = 0; joy_index < num_joysticks; joy_index++)
00219 {
00220 char guid_s[33];
00221 SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(joy_index), guid_s, sizeof(guid_s));
00222 ROS_INFO(" %d: %s %s", joy_index, guid_s, SDL_JoystickNameForIndex(joy_index));
00223
00224 if (joy_index == 0)
00225 {
00226 diag_updater.setHardwareID(guid_s);
00227 }
00228 }
00229
00230
00231
00232
00233 ROS_WARN_COND(num_joysticks > 1, "Connecting to first joystick found.");
00234
00235 ROS_INFO("Successfully connected to %s", SDL_JoystickName(joy_handle));
00236 num_axes = SDL_JoystickNumAxes(joy_handle);
00237 num_buttons = SDL_JoystickNumButtons(joy_handle);
00238 joy_msg.axes.resize(num_axes);
00239 joy_msg.buttons.resize(num_buttons);
00240 return true;
00241 }
00242
00243 void Joystick::Impl::diagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat)
00244 {
00245 if (joy_handle)
00246 {
00247 #if __APPLE__
00248 stat.summary(diagnostic_msgs::DiagnosticStatus::OK,
00249 "Joystick probably connected (disconnect detection does not work on OS X).");
00250 #else
00251 stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "Joystick connected.");
00252 #endif
00253 }
00254 else
00255 {
00256 stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Joystick not connected.");
00257 }
00258 stat.add("Number of detected devices", num_joysticks);
00259 stat.add("Connected device name", joy_handle ? SDL_JoystickName(joy_handle) : "none");
00260 stat.add("Device axes", num_axes);
00261 stat.add("Device buttons", num_buttons);
00262 stat.add("Device power level", joy_handle ? getPowerLevel(joy_handle) : "");
00263 }
00264
00265 double Joystick::Impl::scaleAxis(int32_t unscaled_value)
00266 {
00267 if (unscaled_value > 0)
00268 {
00269 unscaled_value++;
00270 }
00271
00272 double scaled_value = unscaled_value / -32768.0;
00273
00274 if (std::abs(scaled_value) < deadzone)
00275 {
00276 return 0;
00277 }
00278
00279 return scaled_value;
00280 }
00281
00282 const char* Joystick::Impl::getPowerLevel(SDL_Joystick* joy_handle)
00283 {
00284 #ifdef SDL2_ENABLE_JOYSTICK_POWER_LEVEL
00285 switch (SDL_JoystickCurrentPowerLevel(joy_handle))
00286 {
00287 case SDL_JOYSTICK_POWER_UNKNOWN:
00288 return "unknown";
00289 case SDL_JOYSTICK_POWER_EMPTY:
00290 return "empty";
00291 case SDL_JOYSTICK_POWER_LOW:
00292 return "low";
00293 case SDL_JOYSTICK_POWER_MEDIUM:
00294 return "medium";
00295 case SDL_JOYSTICK_POWER_FULL:
00296 return "full";
00297 case SDL_JOYSTICK_POWER_WIRED:
00298 return "wired";
00299 case SDL_JOYSTICK_POWER_MAX:
00300 return "max";
00301 }
00302 #else
00303 return "unsupported";
00304 #endif
00305 }
00306
00307 }