Joystick.h
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @package RobotAPI::ArmarXObjects::GamepadUnit
17  * @author Simon Ottenhaus ( simon dot ottenhaus at kit dot edu )
18  * @date 2017
19  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
23 #pragma once
24 
25 #include <cstdint>
26 
27 #include <fcntl.h>
28 #include <sys/poll.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 
33 
34 #include <linux/joystick.h>
35 
36 namespace armarx
37 {
38 
39  class Joystick
40  {
41 
42  private:
43  int fd = -1;
44  int fdEvent = -1;
45  js_event event;
46 
47  public:
48  std::vector<int16_t> axis;
49  std::vector<bool> buttonsPressed;
52  std::string name;
53 
54  bool
55  open(std::string const& deviceName, std::string const& deviceEventName)
56  {
57 
58  fd = ::open(deviceName.c_str(), O_RDONLY);
59 
60  if (!deviceEventName.empty())
61  {
62  ARMARX_INFO << "Force feedback enabled";
63  fdEvent = ::open(deviceEventName.c_str(), O_RDWR | O_CLOEXEC);
64 
65  ARMARX_CHECK(fdEvent != -1);
66  } else {
67  ARMARX_INFO << "Force feedback disabled";
68  }
69 
70  if (fd != -1)
71  {
72  // ARMARX_INFO << "before";
73  // executeEffect();
74  // ARMARX_INFO << "after";
75 
76 
77  ioctl(fd, JSIOCGAXES, &numberOfAxis);
78  ioctl(fd, JSIOCGBUTTONS, &numberOfButtons);
79  name.resize(255);
80  ioctl(fd, JSIOCGNAME(255), &name[0]);
81  axis.resize(numberOfAxis, 0);
82  name = name.c_str();
83  buttonsPressed.resize(numberOfButtons, false);
84  }
85 
86  ARMARX_INFO << "execute effect";
87  executeEffect();
88 
89  return fd != -1;
90  }
91 
92  bool
93  opened() const
94  {
95  return fd != -1;
96  }
97 
98  bool
100  {
101  int bytes = read(fd, &event, sizeof(event));
102 
103  // NOTE if this condition is not met, we're probably out of sync and this
104  // Joystick instance is likely unusable
105  if (bytes == -1 || bytes != sizeof(event))
106  {
107  return false;
108  }
109 
110  if (event.type & JS_EVENT_BUTTON)
111  {
112  buttonsPressed[event.number] = event.value != 0;
113  }
114  else if (event.type & JS_EVENT_AXIS)
115  {
116  axis[event.number] = event.value;
117  }
118  return true;
119  }
120 
121  void
122  executeEffect(int gain = 100, const int nTimes = 1)
123  {
124  // this feature is disabled
125  if(fdEvent < 0) return;
126 
127  // see https://docs.kernel.org/input/ff.html
128 
129 
130  // https://xnux.eu/devices/feature/vibrator.html
131 
132  int ret;
133  // pollfd pfds[1];
134  int effects;
135 
136  // fd = open_event_dev("vibrator", O_RDWR | O_CLOEXEC);
137  // syscall_error(fd < 0, "Can't open vibrator event device");
138 
139  ret = ioctl(fdEvent, EVIOCGEFFECTS, &effects);
140  ARMARX_CHECK(ret >= 0);
141  // syscall_error(ret < 0, "EVIOCGEFFECTS failed");
142 
143  // ARMARX_CHECK(effects & FF_RUMBLE);
144 
145  // Set the gain of the device
146  {
147  // int gain; between 0 and 100
148  struct input_event ie; // structure used to communicate with the driver
149 
150  ie.type = EV_FF;
151  ie.code = FF_GAIN;
152  ie.value = 0xFFFFUL * gain / 100;
153 
154  if (write(fdEvent, &ie, sizeof(ie)) == -1)
155  {
156  perror("set gain");
157  }
158  }
159 
160 
161  ff_effect e;
162  // e.type = FF_RUMBLE;
163  // e.id = -1;
164  // e.replay.length = 5000;
165  // e.replay.delay = 500;
166  // e.u.rumble.strong_magnitude = 1;
167 
168  e.type = FF_PERIODIC;
169  e.id = -1;
170  e.replay.length = 5000;
171  e.replay.delay = 500;
172  e.u.periodic.waveform = FF_SQUARE;
173  e.u.periodic.period = 1000;
174  e.u.periodic.magnitude = 0xFF;
175  e.u.periodic.offset = 0xFF;
176 
177  ret = ioctl(fdEvent, EVIOCSFF, &e);
178  ARMARX_CHECK(ret >= 0);
179 
180  // syscall_error(ret < 0, "EVIOCSFF failed");
181 
182  ARMARX_INFO << VAROUT(e.id);
183 
184  input_event play;
185  play.type = EV_FF;
186  play.code = static_cast<std::uint16_t>(e.id);
187  play.value = 1;
188 
189  ret = write(fdEvent, &play, sizeof(play));
190  ARMARX_CHECK(ret >= 0);
191 
192  ARMARX_INFO << "Executing effect";
193 
194 
195  for (int i = 0; i < 5; i++)
196  {
197 
198  input_event statusIe; // structure used to communicate with the driver
199  statusIe.type = EV_FF_STATUS;
200  statusIe.code = e.id;
201  // statusIe.value = 0;
202 
203  ret = write(fdEvent, &statusIe, sizeof(statusIe));
204 
205  // ARMARX_CHECK()
206  // ret should be FF_STATUS_PLAYING
207  ARMARX_INFO << VAROUT(ret);
208 
209  sleep(1);
210  }
211 
212 
213  // syscall_error(ret < 0, "write failed");
214 
215  ARMARX_INFO << "Executing effect";
216  // sleep(6);
217 
218 
219  input_event stop;
220  stop.type = EV_FF;
221  stop.code = e.id;
222  stop.value = 0;
223  [[maybe_unused]] const int stopStatus = write(fdEvent, static_cast<const void*>(&stop), sizeof(stop));
224 
225 
226  ret = ioctl(fdEvent, EVIOCRMFF, e.id);
227  ARMARX_CHECK(ret >= 0);
228 
229  // syscall_error(ret < 0, "EVIOCRMFF failed");
230 
231  // close(fdEvent);
232 
233 
234  /**/
235  // Set the gain of the device
236  // {
237  // // int gain; between 0 and 100
238  // struct input_event ie; // structure used to communicate with the driver
239 
240  // ie.type = EV_FF;
241  // ie.code = FF_GAIN;
242  // ie.value = 0xFFFFUL * gain / 100;
243 
244  // if (write(fd, &ie, sizeof(ie)) == -1)
245  // {
246  // perror("set gain");
247  // }
248  // }
249 
250 
251  // struct input_event play;
252  // struct input_event stop;
253 
254  // // upload request to device
255  // ff_effect effect;
256  // const auto uploadStatus = ioctl(fd, EVIOCSFF, &effect);
257 
258  // // Play n times
259  // play.type = EV_FF;
260  // play.code = effect.id;
261  // play.value = nTimes;
262 
263  // const int playStatus = write(fd, static_cast<const void*>(&play), sizeof(play));
264 
265  // // Stop an effect
266  // stop.type = EV_FF;
267  // stop.code = effect.id;
268  // stop.value = 0;
269  // const int stopStatus = write(fd, static_cast<const void*>(&stop), sizeof(stop));
270 
271  // // remove effect
272  // ioctl(fd, EVIOCRMFF, effect.id);
273  }
274 
275  void
277  {
278  ::close(fd);
279  fd = -1;
280  }
281  };
282 } // namespace armarx
armarx::Joystick::open
bool open(std::string const &deviceName, std::string const &deviceEventName)
Definition: Joystick.h:55
armarx::aron::ret
ReaderT::InputType T & ret
Definition: rw.h:21
armarx::Joystick::numberOfButtons
int numberOfButtons
Definition: Joystick.h:51
armarx::Joystick::name
std::string name
Definition: Joystick.h:52
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
armarx::Joystick::executeEffect
void executeEffect(int gain=100, const int nTimes=1)
Definition: Joystick.h:122
armarx::Joystick::buttonsPressed
std::vector< bool > buttonsPressed
Definition: Joystick.h:49
armarx::read
void read(auto &eigen, auto *table)
Definition: FTSensorCalibrationGuiWidgetController.cpp:462
armarx::Joystick::axis
std::vector< int16_t > axis
Definition: Joystick.h:48
armarx::Joystick::close
void close()
Definition: Joystick.h:276
Component.h
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
VAROUT
#define VAROUT(x)
Definition: StringHelpers.h:182
armarx::Joystick::opened
bool opened() const
Definition: Joystick.h:93
armarx::aron::write
requires data::isWriter< WriterT > void write(WriterT &aron_w, const Eigen::Matrix< EigenT, rows, cols, options > &input, typename WriterT::ReturnType &ret, const armarx::aron::Path &aron_p=armarx::aron::Path())
Definition: eigen.h:134
armarx::Joystick::pollEvent
bool pollEvent()
Definition: Joystick.h:99
armarx::Joystick
Definition: Joystick.h:39
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
armarx::Joystick::numberOfAxis
int numberOfAxis
Definition: Joystick.h:50