joystick.cpp
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   // std::string mappings_file;
00076   // nh_param->param<std::string>("mappings_file", mappings_file,
00077   //   ros::package::getPath("joystick_sdl") + "/mappings/gamecontrollerdb.txt");
00078   // pimpl_->addMappingsFromFile(mappings_file);
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     // Don't retry connection too frequently.
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     // Joystick operating normally.
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     // Joystick is disconnected. Send a final message with all buttons
00169     // shown un-pressed. This "releases" any deadman button presses.
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   // Must reinitialize the joystick subsystem in order to scan for newly-connected or
00190   // newly-available devices. Before initializing, must "quit" the subsystem if it was
00191   // previously initialized.
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   // TODO(mikepurvis): This story should be improved, either by an option for it connect to all
00231   // available devices, or a way to differentiate multiple connected ones. At the very least, we
00232   // could allow the user to specify a string to match against the joystick name.
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 }  // namespace joystick_sdl


joystick_sdl
Author(s): Mike Purvis
autogenerated on Fri Feb 12 2016 01:36:48